UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature

1,808 lines 76.7 kB
<!DOCTYPE html>

<html>
<head>
  <title>beam.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>beam.js</h1>
        

        
          <div class="toc">
            <h3>Table of Contents</h3>
            <ol>
              
                
                <li>
                  <a class="source" href="accidental.html">
                    accidental.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="annotation.html">
                    annotation.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="articulation.html">
                    articulation.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="barnote.html">
                    barnote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="beam.html">
                    beam.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="bend.html">
                    bend.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="boundingbox.html">
                    boundingbox.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="boundingboxcomputation.html">
                    boundingboxcomputation.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="canvascontext.html">
                    canvascontext.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="clef.html">
                    clef.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="clefnote.html">
                    clefnote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="crescendo.html">
                    crescendo.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="curve.html">
                    curve.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="dot.html">
                    dot.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="easyscore.html">
                    easyscore.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="element.html">
                    element.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="factory.html">
                    factory.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="formatter.html">
                    formatter.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="fraction.html">
                    fraction.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="frethandfinger.html">
                    frethandfinger.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="ghostnote.html">
                    ghostnote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="glyph.html">
                    glyph.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="gracenote.html">
                    gracenote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="gracenotegroup.html">
                    gracenotegroup.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="gracetabnote.html">
                    gracetabnote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="index.html">
                    index.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="keymanager.html">
                    keymanager.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="keysignature.html">
                    keysignature.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="keysignote.html">
                    keysignote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="modifier.html">
                    modifier.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="modifiercontext.html">
                    modifiercontext.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="multimeasurerest.html">
                    multimeasurerest.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="music.html">
                    music.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="note.html">
                    note.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="notehead.html">
                    notehead.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="notesubgroup.html">
                    notesubgroup.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="ornament.html">
                    ornament.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="parser.html">
                    parser.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="pedalmarking.html">
                    pedalmarking.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="raphaelcontext.html">
                    raphaelcontext.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="registry.html">
                    registry.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="renderer.html">
                    renderer.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stave.html">
                    stave.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stavebarline.html">
                    stavebarline.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="staveconnector.html">
                    staveconnector.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stavehairpin.html">
                    stavehairpin.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="staveline.html">
                    staveline.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stavemodifier.html">
                    stavemodifier.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stavenote.html">
                    stavenote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="staverepetition.html">
                    staverepetition.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stavesection.html">
                    stavesection.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stavetempo.html">
                    stavetempo.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stavetext.html">
                    stavetext.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stavetie.html">
                    stavetie.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stavevolta.html">
                    stavevolta.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stem.html">
                    stem.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stemmablenote.html">
                    stemmablenote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="stringnumber.html">
                    stringnumber.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="strokes.html">
                    strokes.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="svgcontext.html">
                    svgcontext.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="system.html">
                    system.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tables.html">
                    tables.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tabnote.html">
                    tabnote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tabslide.html">
                    tabslide.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tabstave.html">
                    tabstave.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tabtie.html">
                    tabtie.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="textbracket.html">
                    textbracket.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="textdynamics.html">
                    textdynamics.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="textnote.html">
                    textnote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tickable.html">
                    tickable.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tickcontext.html">
                    tickcontext.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="timesignature.html">
                    timesignature.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="timesignote.html">
                    timesignote.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tremolo.html">
                    tremolo.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tuning.html">
                    tuning.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="tuplet.html">
                    tuplet.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="vex.html">
                    vex.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="vibrato.html">
                    vibrato.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="vibratobracket.html">
                    vibratobracket.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="voice.html">
                    voice.js
                  </a>
                </li>
              
                
                <li>
                  <a class="source" href="voicegroup.html">
                    voicegroup.js
                  </a>
                </li>
              
            </ol>
          </div>
        
      </div>

      
        
        <p><a href="http://vexflow.com">VexFlow</a> - Copyright (c) Mohit Muthanna 2010.</p>
<h2 id="description">Description</h2>
<p>This file implements <code>Beams</code> that span over a set of <code>StemmableNotes</code>.</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> { Element } <span class="hljs-keyword">from</span> <span class="hljs-string">'./element'</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> { Tuplet } <span class="hljs-keyword">from</span> <span class="hljs-string">'./tuplet'</span>;
<span class="hljs-keyword">import</span> { Stem } <span class="hljs-keyword">from</span> <span class="hljs-string">'./stem'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateStemDirection</span>(<span class="hljs-params">notes</span>) </span>{
  <span class="hljs-keyword">let</span> lineSum = <span class="hljs-number">0</span>;
  notes.forEach(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (note.keyProps) {
      note.keyProps.forEach(<span class="hljs-function"><span class="hljs-params">keyProp</span> =&gt;</span> {
        lineSum += (keyProp.line - <span class="hljs-number">3</span>);
      });
    }
  });

  <span class="hljs-keyword">if</span> (lineSum &gt;= <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">return</span> Stem.DOWN;
  }
  <span class="hljs-keyword">return</span> Stem.UP;
}

<span class="hljs-keyword">const</span> getStemSlope = <span class="hljs-function">(<span class="hljs-params">firstNote, lastNote</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> firstStemTipY = firstNote.getStemExtents().topY;
  <span class="hljs-keyword">const</span> firstStemX = firstNote.getStemX();
  <span class="hljs-keyword">const</span> lastStemTipY = lastNote.getStemExtents().topY;
  <span class="hljs-keyword">const</span> lastStemX = lastNote.getStemX();
  <span class="hljs-keyword">return</span> (lastStemTipY - firstStemTipY) / (lastStemX - firstStemX);
};

<span class="hljs-keyword">const</span> BEAM_LEFT = <span class="hljs-string">'L'</span>;
<span class="hljs-keyword">const</span> BEAM_RIGHT = <span class="hljs-string">'R'</span>;
<span class="hljs-keyword">const</span> BEAM_BOTH = <span class="hljs-string">'B'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Beam</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Element</span> </span>{</pre></div>
        
      
        
        <p>Gets the default beam groups for a provided time signature.
Attempts to guess if the time signature is not found in table.
Currently this is fairly naive.</p>

        
          <div class='highlight'><pre>  <span class="hljs-keyword">static</span> getDefaultBeamGroups(time_sig) {
    <span class="hljs-keyword">if</span> (!time_sig || time_sig === <span class="hljs-string">'c'</span>) {
      time_sig = <span class="hljs-string">'4/4'</span>;
    }

    <span class="hljs-keyword">const</span> defaults = {
      <span class="hljs-string">'1/2'</span>: [<span class="hljs-string">'1/2'</span>],
      <span class="hljs-string">'2/2'</span>: [<span class="hljs-string">'1/2'</span>],
      <span class="hljs-string">'3/2'</span>: [<span class="hljs-string">'1/2'</span>],
      <span class="hljs-string">'4/2'</span>: [<span class="hljs-string">'1/2'</span>],

      <span class="hljs-string">'1/4'</span>: [<span class="hljs-string">'1/4'</span>],
      <span class="hljs-string">'2/4'</span>: [<span class="hljs-string">'1/4'</span>],
      <span class="hljs-string">'3/4'</span>: [<span class="hljs-string">'1/4'</span>],
      <span class="hljs-string">'4/4'</span>: [<span class="hljs-string">'1/4'</span>],

      <span class="hljs-string">'1/8'</span>: [<span class="hljs-string">'1/8'</span>],
      <span class="hljs-string">'2/8'</span>: [<span class="hljs-string">'2/8'</span>],
      <span class="hljs-string">'3/8'</span>: [<span class="hljs-string">'3/8'</span>],
      <span class="hljs-string">'4/8'</span>: [<span class="hljs-string">'2/8'</span>],

      <span class="hljs-string">'1/16'</span>: [<span class="hljs-string">'1/16'</span>],
      <span class="hljs-string">'2/16'</span>: [<span class="hljs-string">'2/16'</span>],
      <span class="hljs-string">'3/16'</span>: [<span class="hljs-string">'3/16'</span>],
      <span class="hljs-string">'4/16'</span>: [<span class="hljs-string">'2/16'</span>],
    };

    <span class="hljs-keyword">const</span> groups = defaults[time_sig];

    <span class="hljs-keyword">if</span> (groups === <span class="hljs-literal">undefined</span>) {</pre></div>
        
      
        
        <p>If no beam groups found, naively determine
the beam groupings from the time signature</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> beatTotal = <span class="hljs-built_in">parseInt</span>(time_sig.split(<span class="hljs-string">'/'</span>)[<span class="hljs-number">0</span>], <span class="hljs-number">10</span>);
      <span class="hljs-keyword">const</span> beatValue = <span class="hljs-built_in">parseInt</span>(time_sig.split(<span class="hljs-string">'/'</span>)[<span class="hljs-number">1</span>], <span class="hljs-number">10</span>);

      <span class="hljs-keyword">const</span> tripleMeter = beatTotal % <span class="hljs-number">3</span> === <span class="hljs-number">0</span>;

      <span class="hljs-keyword">if</span> (tripleMeter) {
        <span class="hljs-keyword">return</span> [<span class="hljs-keyword">new</span> Fraction(<span class="hljs-number">3</span>, beatValue)];
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (beatValue &gt; <span class="hljs-number">4</span>) {
        <span class="hljs-keyword">return</span> [<span class="hljs-keyword">new</span> Fraction(<span class="hljs-number">2</span>, beatValue)];
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (beatValue &lt;= <span class="hljs-number">4</span>) {
        <span class="hljs-keyword">return</span> [<span class="hljs-keyword">new</span> Fraction(<span class="hljs-number">1</span>, beatValue)];
      }
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> groups.map(<span class="hljs-function"><span class="hljs-params">group</span> =&gt;</span> <span class="hljs-keyword">new</span> Fraction().parse(group));
    }

    <span class="hljs-keyword">return</span> [<span class="hljs-keyword">new</span> Fraction(<span class="hljs-number">1</span>, <span class="hljs-number">4</span>)];
  }</pre></div>
        
      
        
        <p>A helper function to automatically build basic beams for a voice. For more
complex auto-beaming use <code>Beam.generateBeams()</code>.</p>
<p>Parameters:</p>
<ul>
<li><code>voice</code> - The voice to generate the beams for</li>
<li><code>stem_direction</code> - A stem direction to apply to the entire voice</li>
<li><code>groups</code> - An array of <code>Fraction</code> representing beat groupings for the beam</li>
</ul>

        
          <div class='highlight'><pre>  <span class="hljs-keyword">static</span> applyAndGetBeams(voice, stem_direction, groups) {
    <span class="hljs-keyword">return</span> Beam.generateBeams(voice.getTickables(), {
      groups,
      stem_direction,
    });
  }</pre></div>
        
      
        
        <p>A helper function to autimatically build beams for a voice with
configuration options.</p>
<p>Example configuration object:</p>
<pre><code>config = {
  <span class="hljs-attr">groups</span>: [<span class="hljs-keyword">new</span> Vex.Flow.Fraction(<span class="hljs-number">2</span>, <span class="hljs-number">8</span>)],
  <span class="hljs-attr">stem_direction</span>: <span class="hljs-number">-1</span>,
  <span class="hljs-attr">beam_rests</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">beam_middle_only</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">show_stemlets</span>: <span class="hljs-literal">false</span>
};</code></pre><p>Parameters:</p>
<ul>
<li><code>notes</code> - An array of notes to create the beams for</li>
<li><code>config</code> - The configuration object<ul>
<li><code>groups</code> - Array of <code>Fractions</code> that represent the beat structure to beam the notes</li>
<li><code>stem_direction</code> - Set to apply the same direction to all notes</li>
<li><code>beam_rests</code> - Set to <code>true</code> to include rests in the beams</li>
<li><code>beam_middle_only</code> - Set to <code>true</code> to only beam rests in the middle of the beat</li>
<li><code>show_stemlets</code> - Set to <code>true</code> to draw stemlets for rests</li>
<li><code>maintain_stem_directions</code> - Set to <code>true</code> to not apply new stem directions</li>
</ul>
</li>
</ul>

        
          <div class='highlight'><pre>  <span class="hljs-keyword">static</span> generateBeams(notes, config) {
    <span class="hljs-keyword">if</span> (!config) config = {};

    <span class="hljs-keyword">if</span> (!config.groups || !config.groups.length) {
      config.groups = [<span class="hljs-keyword">new</span> Fraction(<span class="hljs-number">2</span>, <span class="hljs-number">8</span>)];
    }</pre></div>
        
      
        
        <p>Convert beam groups to tick amounts</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> tickGroups = config.groups.map(<span class="hljs-function"><span class="hljs-params">group</span> =&gt;</span> {
      <span class="hljs-keyword">if</span> (!group.multiply) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RuntimeError(<span class="hljs-string">'InvalidBeamGroups'</span>,
          <span class="hljs-string">'The beam groups must be an array of Vex.Flow.Fractions'</span>);
      }
      <span class="hljs-keyword">return</span> group.clone().multiply(Flow.RESOLUTION, <span class="hljs-number">1</span>);
    });

    <span class="hljs-keyword">const</span> unprocessedNotes = notes;
    <span class="hljs-keyword">let</span> currentTickGroup = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">let</span> noteGroups = [];
    <span class="hljs-keyword">let</span> currentGroup = [];

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTotalTicks</span>(<span class="hljs-params">vf_notes</span>) </span>{
      <span class="hljs-keyword">return</span> vf_notes.reduce(<span class="hljs-function">(<span class="hljs-params">memo, note</span>) =&gt;</span> note.getTicks().clone().add(memo), <span class="hljs-keyword">new</span> Fraction(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>));
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">nextTickGroup</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">if</span> (tickGroups.length - <span class="hljs-number">1</span> &gt; currentTickGroup) {
        currentTickGroup += <span class="hljs-number">1</span>;
      } <span class="hljs-keyword">else</span> {
        currentTickGroup = <span class="hljs-number">0</span>;
      }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createGroups</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">let</span> nextGroup = [];

      unprocessedNotes.forEach(<span class="hljs-function"><span class="hljs-params">unprocessedNote</span> =&gt;</span> {
        nextGroup = [];
        <span class="hljs-keyword">if</span> (unprocessedNote.shouldIgnoreTicks()) {
          noteGroups.push(currentGroup);
          currentGroup = nextGroup;
          <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Ignore untickables (like bar notes)</span>
        }

        currentGroup.push(unprocessedNote);
        <span class="hljs-keyword">const</span> ticksPerGroup = tickGroups[currentTickGroup].clone();
        <span class="hljs-keyword">const</span> totalTicks = getTotalTicks(currentGroup);</pre></div>
        
      
        
        <p>Double the amount of ticks in a group, if it’s an unbeamable tuplet</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">const</span> unbeamable = Flow.durationToNumber(unprocessedNote.duration) &lt; <span class="hljs-number">8</span>;
        <span class="hljs-keyword">if</span> (unbeamable &amp;&amp; unprocessedNote.tuplet) {
          ticksPerGroup.numerator *= <span class="hljs-number">2</span>;
        }</pre></div>
        
      
        
        <p>If the note that was just added overflows the group tick total</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">if</span> (totalTicks.greaterThan(ticksPerGroup)) {</pre></div>
        
      
        
        <p>If the overflow note can be beamed, start the next group
with it. Unbeamable notes leave the group overflowed.</p>

        
          <div class='highlight'><pre>          <span class="hljs-keyword">if</span> (!unbeamable) {
            nextGroup.push(currentGroup.pop());
          }
          noteGroups.push(currentGroup);
          currentGroup = nextGroup;
          nextTickGroup();
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (totalTicks.equals(ticksPerGroup)) {
          noteGroups.push(currentGroup);
          currentGroup = nextGroup;
          nextTickGroup();
        }
      });</pre></div>
        
      
        
        <p>Adds any remainder notes</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (currentGroup.length &gt; <span class="hljs-number">0</span>) {
        noteGroups.push(currentGroup);
      }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBeamGroups</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">return</span> noteGroups.filter(<span class="hljs-function"><span class="hljs-params">group</span> =&gt;</span> {
        <span class="hljs-keyword">if</span> (group.length &gt; <span class="hljs-number">1</span>) {
          <span class="hljs-keyword">let</span> beamable = <span class="hljs-literal">true</span>;
          group.forEach(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> {
            <span class="hljs-keyword">if</span> (note.getIntrinsicTicks() &gt;= Flow.durationToTicks(<span class="hljs-string">'4'</span>)) {
              beamable = <span class="hljs-literal">false</span>;
            }
          });
          <span class="hljs-keyword">return</span> beamable;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      });
    }</pre></div>
        
      
        
        <p>Splits up groups by Rest</p>

        
          <div class='highlight'><pre>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sanitizeGroups</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">const</span> sanitizedGroups = [];
      noteGroups.forEach(<span class="hljs-function"><span class="hljs-params">group</span> =&gt;</span> {
        <span class="hljs-keyword">let</span> tempGroup = [];
        group.forEach(<span class="hljs-function">(<span class="hljs-params">note, index, group</span>) =&gt;</span> {
          <span class="hljs-keyword">const</span> isFirstOrLast = index === <span class="hljs-number">0</span> || index === group.length - <span class="hljs-number">1</span>;
          <span class="hljs-keyword">const</span> prevNote = group[index - <span class="hljs-number">1</span>];

          <span class="hljs-keyword">const</span> breaksOnEachRest = !config.beam_rests &amp;&amp; note.isRest();
          <span class="hljs-keyword">const</span> breaksOnFirstOrLastRest = (config.beam_rests &amp;&amp;
            config.beam_middle_only &amp;&amp; note.isRest() &amp;&amp; isFirstOrLast);

          <span class="hljs-keyword">let</span> breakOnStemChange = <span class="hljs-literal">false</span>;
          <span class="hljs-keyword">if</span> (config.maintain_stem_directions &amp;&amp; prevNote &amp;&amp;
            !note.isRest() &amp;&amp; !prevNote.isRest()) {
            <span class="hljs-keyword">const</span> prevDirection = prevNote.getStemDirection();
            <span class="hljs-keyword">const</span> currentDirection = note.getStemDirection();
            breakOnStemChange = currentDirection !== prevDirection;
          }

          <span class="hljs-keyword">const</span> isUnbeamableDuration = <span class="hljs-built_in">parseInt</span>(note.duration, <span class="hljs-number">10</span>) &lt; <span class="hljs-number">8</span>;</pre></div>
        
      
        
        <p>Determine if the group should be broken at this note</p>

        
          <div class='highlight'><pre>          <span class="hljs-keyword">const</span> shouldBreak = breaksOnEachRest || breaksOnFirstOrLastRest ||
            breakOnStemChange || isUnbeamableDuration;

          <span class="hljs-keyword">if</span> (shouldBreak) {</pre></div>
        
      
        
        <p>Add current group</p>

        
          <div class='highlight'><pre>            <span class="hljs-keyword">if</span> (tempGroup.length &gt; <span class="hljs-number">0</span>) {
              sanitizedGroups.push(tempGroup);
            }</pre></div>
        
      
        
        <p>Start a new group. Include the current note if the group
was broken up by stem direction, as that note needs to start
the next group of notes</p>

        
          <div class='highlight'><pre>            tempGroup = breakOnStemChange ? [note] : [];
          } <span class="hljs-keyword">else</span> {</pre></div>
        
      
        
        <p>Add note to group</p>

        
          <div class='highlight'><pre>            tempGroup.push(note);
          }
        });</pre></div>
        
      
        
        <p>If there is a remaining group, add it as well</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">if</span> (tempGroup.length &gt; <span class="hljs-number">0</span>) {
          sanitizedGroups.push(tempGroup);
        }
      });

      noteGroups = sanitizedGroups;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">formatStems</span>(<span class="hljs-params"></span>) </span>{
      noteGroups.forEach(<span class="hljs-function"><span class="hljs-params">group</span> =&gt;</span> {
        <span class="hljs-keyword">let</span> stemDirection;
        <span class="hljs-keyword">if</span> (config.maintain_stem_directions) {
          <span class="hljs-keyword">const</span> note = findFirstNote(group);
          stemDirection = note ? note.getStemDirection() : Stem.UP;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">if</span> (config.stem_direction) {
            stemDirection = config.stem_direction;
          } <span class="hljs-keyword">else</span> {
            stemDirection = calculateStemDirection(group);
          }
        }
        applyStemDirection(group, stemDirection);
      });
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">findFirstNote</span>(<span class="hljs-params">group</span>) </span>{
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; group.length; i++) {
        <span class="hljs-keyword">const</span> note = group[i];
        <span class="hljs-keyword">if</span> (!note.isRest()) {
          <span class="hljs-keyword">return</span> note;
        }
      }

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

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">applyStemDirection</span>(<span class="hljs-params">group, direction</span>) </span>{
      group.forEach(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> {
        note.setStemDirection(direction);
      });
    }</pre></div>
        
      
        
        <p>Get all of the tuplets in all of the note groups</p>

        
          <div class='highlight'><pre>    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTuplets</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">const</span> uniqueTuplets = [];</pre></div>
        
      
        
        <p>Go through all of the note groups and inspect for tuplets</p>

        
          <div class='highlight'><pre>      noteGroups.forEach(<span class="hljs-function"><span class="hljs-params">group</span> =&gt;</span> {
        <span class="hljs-keyword">let</span> tuplet = <span class="hljs-literal">null</span>;
        group.forEach(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> {
          <span class="hljs-keyword">if</span> (note.tuplet &amp;&amp; (tuplet !== note.tuplet)) {
            tuplet = note.tuplet;
            uniqueTuplets.push(tuplet);
          }
        });
      });
      <span class="hljs-keyword">return</span> uniqueTuplets;
    }</pre></div>
        
      
        
        <p>Using closures to store the variables throughout the various functions
IMO Keeps it this process lot cleaner - but not super consistent with
the rest of the API’s style - Silverwolf90 (Cyril)</p>

        
          <div class='highlight'><pre>    createGroups();
    sanitizeGroups();
    formatStems();</pre></div>
        
      
        
        <p>Get the notes to be beamed</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> beamedNoteGroups = getBeamGroups();</pre></div>
        
      
        
        <p>Get the tuplets in order to format them accurately</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> allTuplets = getTuplets();</pre></div>
        
      
        
        <p>Create a Vex.Flow.Beam from each group of notes to be beamed</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> beams = [];
    beamedNoteGroups.forEach(<span class="hljs-function"><span class="hljs-params">group</span> =&gt;</span> {
      <span class="hljs-keyword">const</span> beam = <span class="hljs-keyword">new</span> Beam(group);

      <span class="hljs-keyword">if</span> (config.show_stemlets) {
        beam.render_options.show_stemlets = <span class="hljs-literal">true</span>;
      }
      <span class="hljs-keyword">if</span> (config.secondary_breaks) {
        beam.render_options.secondary_break_ticks = Flow.durationToTicks(config.secondary_breaks);
      }
      <span class="hljs-keyword">if</span> (config.flat_beams === <span class="hljs-literal">true</span>) {
        beam.render_options.flat_beams = <span class="hljs-literal">true</span>;
        beam.render_options.flat_beam_offset = config.flat_beam_offset;
      }
      beams.push(beam);
    });</pre></div>
        
      
        
        <p>Reformat tuplets</p>

        
          <div class='highlight'><pre>    allTuplets.forEach(<span class="hljs-function"><span class="hljs-params">tuplet</span> =&gt;</span> {</pre></div>
        
      
        
        <p>Set the tuplet location based on the stem direction</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> direction = tuplet.notes[<span class="hljs-number">0</span>].stem_direction === Stem.DOWN ?
        Tuplet.LOCATION_BOTTOM : Tuplet.LOCATION_TOP;
      tuplet.setTupletLocation(direction);</pre></div>
        
      
        
        <p>If any of the notes in the tuplet are not beamed, draw a bracket.</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">let</span> bracketed = <span class="hljs-literal">false</span>;
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; tuplet.notes.length; i++) {
        <span class="hljs-keyword">const</span> note = tuplet.notes[i];
        <span class="hljs-keyword">if</span> (note.beam === <span class="hljs-literal">null</span>) {
          bracketed = <span class="hljs-literal">true</span>;
          <span class="hljs-keyword">break</span>;
        }
      }
      tuplet.setBracketed(bracketed);
    });

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

  <span class="hljs-keyword">constructor</span>(notes, auto_stem) {
    <span class="hljs-keyword">super</span>();
    <span class="hljs-keyword">this</span>.setAttribute(<span class="hljs-string">'type'</span>, <span class="hljs-string">'Beam'</span>);

    <span class="hljs-keyword">if</span> (!notes || notes === []) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RuntimeError(<span class="hljs-string">'BadArguments'</span>, <span class="hljs-string">'No notes provided for beam.'</span>);
    }

    <span class="hljs-keyword">if</span> (notes.length === <span class="hljs-number">1</span>) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RuntimeError(<span class="hljs-string">'BadArguments'</span>, <span class="hljs-string">'Too few notes for beam.'</span>);
    }</pre></div>
        
      
        
        <p>Validate beam line, direction and ticks.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">this</span>.ticks = notes[<span class="hljs-number">0</span>].getIntrinsicTicks();

    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.ticks &gt;= Flow.durationToTicks(<span class="hljs-string">'4'</span>)) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Vex.RuntimeError(<span class="hljs-string">'BadArguments'</span>,
        <span class="hljs-string">'Beams can only be applied to notes shorter than a quarter note.'</span>);
    }

    <span class="hljs-keyword">let</span> i; <span class="hljs-comment">// shared iterator</span>
    <span class="hljs-keyword">let</span> note;

    <span class="hljs-keyword">this</span>.stem_direction = Stem.UP;

    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; notes.length; ++i) {
      note = notes[i];
      <span class="hljs-keyword">if</span> (note.hasStem()) {
        <span class="hljs-keyword">this</span>.stem_direction = note.getStemDirection();
        <span class="hljs-keyword">break</span>;
      }
    }

    <span class="hljs-keyword">let</span> stem_direction = <span class="hljs-keyword">this</span>.stem_direction;</pre></div>
        
      
        
        <p>Figure out optimal stem direction based on given notes</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">if</span> (auto_stem &amp;&amp; notes[<span class="hljs-number">0</span>].getCategory() === <span class="hljs-string">'stavenotes'</span>) {
      stem_direction = calculateStemDirection(notes);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (auto_stem &amp;&amp; notes[<span class="hljs-number">0</span>].getCategory() === <span class="hljs-string">'tabnotes'</span>) {</pre></div>
        
      
        
        <p>Auto Stem TabNotes</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> stem_weight = notes.reduce(<span class="hljs-function">(<span class="hljs-params">memo, note</span>) =&gt;</span> memo + note.stem_direction, <span class="hljs-number">0</span>);

      stem_direction = stem_weight &gt; <span class="hljs-number">-1</span> ? Stem.UP : Stem.DOWN;
    }</pre></div>
        
      
        
        <p>Apply stem directions and attach beam to notes</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i &lt; notes.length; ++i) {
      note = notes[i];
      <span class="hljs-keyword">if</span> (auto_stem) {
        note.setStemDirection(stem_direction);
        <span class="hljs-keyword">this</span>.stem_direction = stem_direction;
      }
      note.setBeam(<span class="hljs-keyword">this</span>);
    }

    <span class="hljs-keyword">this</span>.postFormatted = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">this</span>.notes = notes;
    <span class="hljs-keyword">this</span>.beam_count = <span class="hljs-keyword">this</span>.getBeamCount();
    <span class="hljs-keyword">this</span>.break_on_indices = [];
    <span class="hljs-keyword">this</span>.render_options = {
      <span class="hljs-attr">beam_width</span>: <span class="hljs-number">5</span>,
      <span class="hljs-attr">max_slope</span>: <span class="hljs-number">0.25</span>,
      <span class="hljs-attr">min_slope</span>: <span class="hljs-number">-0.25</span>,
      <span class="hljs-attr">slope_iterations</span>: <span class="hljs-number">20</span>,
      <span class="hljs-attr">slope_cost</span>: <span class="hljs-number">100</span>,
      <span class="hljs-attr">show_stemlets</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">stemlet_extension</span>: <span class="hljs-number">7</span>,
      <span class="hljs-attr">partial_beam_length</span>: <span class="hljs-number">10</span>,
      <span class="hljs-attr">flat_beams</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">min_flat_beam_offset</span>: <span class="hljs-number">15</span>,
    };
  }</pre></div>
        
      
        
        <p>Get the notes in this beam</p>

        
          <div class='highlight'><pre>  getNotes() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.notes; }</pre></div>
        
      
        
        <p>Get the max number of beams in the set of notes</p>

        
          <div class='highlight'><pre>  getBeamCount() {
    <span class="hljs-keyword">const</span> beamCounts = <span class="hljs-keyword">this</span>.notes.map(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> note.getGlyph().beam_count);

    <span class="hljs-keyword">const</span> maxBeamCount = beamCounts.reduce(<span class="hljs-function">(<span class="hljs-params">max, beamCount</span>) =&gt;</span> beamCount &gt; max ? beamCount : max);

    <span class="hljs-keyword">return</span> maxBeamCount;
  }</pre></div>
        
      
        
        <p>Set which note <code>indices</code> to break the secondary beam at</p>

        
          <div class='highlight'><pre>  breakSecondaryAt(indices) {
    <span class="hljs-keyword">this</span>.break_on_indices = indices;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
  }</pre></div>
        
      
        
        <p>Return the y coordinate for linear function</p>

        
          <div class='highlight'><pre>  getSlopeY(x, first_x_px, first_y_px, slope) {
    <span class="hljs-keyword">return</span> first_y_px + ((x - first_x_px) * slope);
  }</pre></div>
        
      
        
        <p>Calculate the best possible slope for the provided notes</p>

        
          <div class='highlight'><pre>  calculateSlope() {
    <span class="hljs-keyword">const</span> {
      notes,
      <span class="hljs-attr">stem_direction</span>: stemDirection,
      <span class="hljs-attr">render_options</span>: { max_slope, min_slope, slope_iterations, slope_cost },
    } = <span class="hljs-keyword">this</span>;

    <span class="hljs-keyword">const</span> firstNote = notes[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">const</span> initialSlope = getStemSlope(firstNote, notes[notes.length - <span class="hljs-number">1</span>]);
    <span class="hljs-keyword">const</span> increment = (max_slope - min_slope) / slope_iterations;
    <span class="hljs-keyword">let</span> minCost = <span class="hljs-built_in">Number</span>.MAX_VALUE;
    <span class="hljs-keyword">let</span> bestSlope = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">let</span> yShift = <span class="hljs-number">0</span>;</pre></div>
        
      
        
        <p>iterate through slope values to find best weighted fit</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> slope = min_slope; slope &lt;= max_slope; slope += increment) {
      <span class="hljs-keyword">let</span> totalStemExtension = <span class="hljs-number">0</span>;
      <span class="hljs-keyword">let</span> yShiftTemp = <span class="hljs-number">0</span>;</pre></div>
        
      
        
        <p>iterate through notes, calculating y shift and stem extension</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">1</span>; i &lt; notes.length; ++i) {
        <span class="hljs-keyword">const</span> note = notes[i];
        <span class="hljs-keyword">const</span> adjustedStemTipY = <span class="hljs-keyword">this</span>.getSlopeY(
          note.getStemX(),
          firstNote.getStemX(),
          firstNote.getStemExtents().topY,
          slope
        ) + yShiftTemp;

        <span class="hljs-keyword">const</span> stemTipY = note.getStemExtents().topY;</pre></div>
        
      
        
        <p>beam needs to be shifted up to accommodate note</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">if</span> (stemTipY * stemDirection &lt; adjustedStemTipY * stemDirection) {
          <span class="hljs-keyword">const</span> diff = <span class="hljs-built_in">Math</span>.abs(stemTipY - adjustedStemTipY);
          yShiftTemp += diff * -stemDirection;
          totalStemExtension += diff * i;
        } <span class="hljs-keyword">else</span> { <span class="hljs-comment">// beam overshoots note, account for the difference</span>
          totalStemExtension += (stemTipY - adjustedStemTipY) * stemDirection;
        }
      }</pre></div>
        
      
        
        <p>most engraving books suggest aiming for a slope about half the angle of the
difference between the first and last notes’ stem length;</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> idealSlope = initialSlope / <span class="hljs-number">2</span>;
      <span class="hljs-keyword">const</span> distanceFromIdeal = <span class="hljs-built_in">Math</span>.abs(idealSlope - slope);</pre></div>
        
      
        
        <p>This tries to align most beams to something closer to the idealSlope, but
doesn’t go crazy. To disable, set this.render_options.slope_cost = 0</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> cost = slope_cost * distanceFromIdeal + <span class="hljs-built_in">Math</span>.abs(totalStemExtension);</pre></div>
        
      
        
        <p>update state when a more ideal slope is found</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (cost &lt; minCost) {
        minCost = cost;
        bestSlope = slope;
        yShift = yShiftTemp;
      }
    }

    <span class="hljs-keyword">this</span>.slope = bestSlope;
    <span class="hljs-keyword">this</span>.y_shift = yShift;
  }</pre></div>
        
      
        
        <p>Calculate a slope and y-shift for flat beams</p>

        
          <div class='highlight'><pre>  calculateFlatSlope() {
    <span class="hljs-keyword">const</span> {
      notes, stem_direction,
      <span class="hljs-attr">render_options</span>: { beam_width, min_flat_beam_offset, flat_beam_offset },
    } = <span class="hljs-keyword">this</span>;</pre></div>
        
      
        
        <p>If a flat beam offset has not yet been supplied or calculated,
generate one based on the notes in this particular note group</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">let</span> total = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">let</span> extremeY = <span class="hljs-number">0</span>;  <span class="hljs-comment">// Store the highest or lowest note here</span>
    <span class="hljs-keyword">let</span> extremeBeamCount = <span class="hljs-number">0</span>;  <span class="hljs-comment">// The beam count of the extreme note</span>
    <span class="hljs-keyword">let</span> currentExtreme = <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; notes.length; i++) {</pre></div>
        
      
        
        <p>Total up all of the offsets so we can average them out later</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> note = notes[i];
      <span class="hljs-keyword">const</span> stemTipY = note.getStemExtents().topY;
      total += stemTipY;</pre></div>
        
      
        
        <p>Store the highest (stems-up) or lowest (stems-down) note so the
 offset can be adjusted in case the average isn’t enough</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (stem_direction === Stem.DOWN &amp;&amp; currentExtreme &lt; stemTipY) {
        currentExtreme = stemTipY;
        extremeY = <span class="hljs-built_in">Math</span>.max(...note.getYs());
        extremeBeamCount = note.getBeamCount();
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (
        stem_direction === Stem.UP &amp;&amp; (currentExtreme === <span class="hljs-number">0</span> || currentExtreme &gt; stemTipY)
      ) {
        currentExtreme = stemTipY;
        extremeY = <span class="hljs-built_in">Math</span>.min(...note.getYs());
        extremeBeamCount = note.getBeamCount();
      }
    }</pre></div>
        
      
        
        <p>Average the offsets to try and come up with a reasonable one that
 works for all of the notes in the beam group.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">let</span> offset = total / notes.length;</pre></div>
        
      
        
        <p>In case the average isn’t long enough, add or subtract some more
 based on the highest or lowest note (again, based on the stem
 direction). This also takes into account the added height due to
 the width of the beams.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> beamWidth = beam_width * <span class="hljs-number">1.5</span>;
    <span class="hljs-keyword">const</span> extremeTest = min_flat_beam_offset + (extremeBeamCount * beamWidth);
    <span class="hljs-keyword">const</span> newOffset = extremeY + (extremeTest * -stem_direction);
    <span class="hljs-keyword">if</span> (stem_direction === Stem.DOWN &amp;&amp; offset &lt; newOffset) {
      offset = extremeY + extremeTest;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (stem_direction === Stem.UP &amp;&amp; offset &gt; newOffset) {
      offset = extremeY - extremeTest;
    }

    <span class="hljs-keyword">if</span> (!flat_beam_offset) {</pre></div>
        
      
        
        <p>Set the offset for the group based on the calculations above.</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">this</span>.render_options.flat_beam_offset = offset;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (stem_direction === Stem.DOWN &amp;&amp; offset &gt; flat_beam_offset) {
      <span class="hljs-keyword">this</span>.render_options.flat_beam_offset = offset;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (stem_direction === Stem.UP &amp;&amp; offset &lt; flat_beam_offset) {
      <span class="hljs-keyword">this</span>.render_options.flat_beam_offset = offset;
    }</pre></div>
        
      
        
        <p>for flat beams, the slope and y_shift are simply 0</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">this</span>.slope = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">this</span>.y_shift = <span class="hljs-number">0</span>;
  }

  getBeamYToDraw() {
    <span class="hljs-keyword">const</span> firstNote = <span class="hljs-keyword">this</span>.notes[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">const</span> firstStemTipY = firstNote.getStemExtents().topY;
    <span class="hljs-keyword">let</span> beamY = firstStemTipY;</pre></div>
        
      
        
        <p>For flat beams, set the first and last Y to the offset, rather than
 using the note’s stem extents.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.render_options.flat_beams &amp;&amp; <span class="hljs-keyword">this</span>.render_options.flat_beam_offset) {
      beamY = <span class="hljs-keyword">this</span>.render_options.flat_beam_offset;
    }
    <span class="hljs-keyword">return</span> beamY;
  }</pre></div>
        
      
        
        <p>Create new stems for the notes in the beam, so that each stem
extends into the beams.</p>

        
          <div class='highlight'><pre>  applyStemExtensions() {
    <span class="hljs-keyword">const</span> {
      notes, slope, y_shift, stem_direction, beam_count,
      <span class="hljs-attr">render_options</span>: {
        show_stemlets,
        stemlet_extension,
        beam_width,
      },
    } = <span class="hljs-keyword">this</span>;

    <span class="hljs-keyword">const</span> firstNote = notes[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">const</span> firstStemTipY = <span class="hljs-keyword">this</span>.getBeamYToDraw();
    <span class="hljs-keyword">const</span> firstStemX = firstNote.getStemX();

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; notes.length; ++i) {
      <span class="hljs-keyword">const</span> note = notes[i];
      <span class="hljs-keyword">const</span> stemX = note.getStemX();
      <span class="hljs-keyword">const</span> { <span class="hljs-attr">topY</span>: stemTipY } = note.getStemExtents();
      <span class="hljs-keyword">const</span> beamedStemTipY = <span class="hljs-keyword">this</span>.getSlopeY(stemX, firstStemX, firstStemTipY, slope) + y_shift;
      <span class="hljs-keyword">const</span> preBeamExtension = note.getStem().getExtension();
      <span class="hljs-keyword">const</span> beamExtension = stem_direction === Stem.UP
        ? stemTipY - beamedStemTipY
        : beamedStemTipY - stemTipY;

      note.stem.setExtension(preBeamExtension + beamExtension);
      note.stem.renderHeightAdjustment = -Stem.WIDTH / <span class="hljs-number">2</span>;

      <span class="hljs-keyword">if</span> (note.isRest() &amp;&amp; show_stemlets) {
        <span class="hljs-keyword">const</span> beamWidth = beam_width;
        <span class="hljs-keyword">const</span> totalBeamWidth = ((beam_count - <span class="hljs-number">1</span>) * beamWidth * <span class="hljs-number">1.5</span>) + beamWidth;
        note.stem
          .setVisibility(<span class="hljs-literal">true</span>)
          .setStemlet(<span class="hljs-literal">true</span>, totalBeamWidth + stemlet_extension);
      }
    }
  }</pre></div>
        
      
        
        <p>return upper level beam direction.</p>

        
          <div class='highlight'><pre>  lookupBeamDirection(duration, prev_tick, tick, next_tick) {
    if (duration === '4') {
      return BEAM_LEFT;
    }

    const lookup_duration =  `${Flow.durationToNumber(duration) / 2}`;
    const prev_note_gets_beam = prev_tick &lt; Flow.durationToTicks(lookup_duration);
    const next_note_gets_beam = next_tick &lt; Flow.durationToTicks(lookup_duration);
    const note_gets_beam = tick &lt; Flow.durationToTicks(lookup_duration);

    if (prev_note_gets_beam &amp;&amp; next_note_gets_beam &amp;&amp; note_gets_beam) {
      return BEAM_BOTH;
    } else if (prev_note_gets_beam &amp;&amp; !next_note_gets_beam &amp;&amp; note_gets_beam) {
      return BEAM_LEFT;
    } else if (!prev_note_gets_beam &amp;&amp; next_note_gets_beam &amp;&amp; note_gets_beam) {
      return BEAM_RIGHT;
    }

    return this.lookupBeamDirection(lookup_duration, prev_tick, tick, next_tick);
  }</pre></div>
        
      
        
        <p>Get the x coordinates for the beam lines of specific <code>duration</code></p>

        
          <div class='highlight'><pre>  getBeamLines(duration) {
    <span class="hljs-keyword">const</span> tick_of_duration = Flow.durationToTicks(duration);
    <span class="hljs-keyword">const</span> beam_lines = [];
    <span class="hljs-keyword">let</span> beam_started = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">let</span> current_beam = <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">const</span> partial_beam_length = <span class="hljs-keyword">this</span>.render_options.partial_beam_length;
    <span class="hljs-keyword">let</span> previous_should_break = <span class="hljs-literal">false</span>;
    <span class="hljs-keyword">let</span> tick_tally = <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>.notes.length; ++i) {
      <span class="hljs-keyword">const</span> note = <span class="hljs-keyword">this</span>.notes[i];</pre></div>
        
      
        
        <p>See if we need to break secondary beams on this note.</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> ticks = note.ticks.value();
      tick_tally += ticks;
      <span class="hljs-keyword">let</span> should_break = <span class="hljs-literal">false</span>;</pre></div>
        
      
        
        <p>8th note beams are always drawn.</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">parseInt</span>(duration, <span class="hljs-number">10</span>) &gt;= <span class="hljs-number">8</span>) {</pre></div>
        
      
        
        <p>First, check to see if any indices were set up through breakSecondaryAt()</p>

        
          <div class='highlight'><pre>        should_break = <span class="hljs-keyword">this</span>.break_on_indices.indexOf(i) !== <span class="hljs-number">-1</span>;</pre></div>
        
      
        
        <p>If the secondary breaks were auto-configured in the render options,
 handle that as well.</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.render_options.secondary_break_ticks &amp;&amp; tick_tally &gt;=
          <span class="hljs-keyword">this</span>.render_options.secondary_break_ticks) {
          tick_tally = <span class="hljs-number">0</span>;
          should_break = <span class="hljs-literal">true</span>;
        }
      }
      <span class="hljs-keyword">const</span> note_gets_beam = note.getIntrinsicTicks() &lt; tick_of_duration;

      <span class="hljs-keyword">const</span> stem_x = note.getStemX() - (Stem.WIDTH / <span class="hljs-number">2</span>);</pre></div>
        
      
        
        <p>Check to see if the next note in the group will get a beam at this
 level. This will help to inform the partial beam logic below.</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">const</span> prev_note = <span class="hljs-keyword">this</span>.notes[i - <span class="hljs-number">1</span>];
      <span class="hljs-keyword">const</span> next_note = <span class="hljs-keyword">this</span>.notes[i + <span class="hljs-number">1</span>];
      <span class="hljs-keyword">const</span> next_note_gets_beam = next_note &amp;&amp; next_note.getIntrinsicTicks() &lt; tick_of_duration;
      <span class="hljs-keyword">const</span> prev_note_gets_beam = prev_note &amp;&amp; prev_note.getIntrinsicTicks() &lt; tick_of_duration;
      <span class="hljs-keyword">const</span> beam_alone = prev_note &amp;&amp; next_note &amp;&amp;
      note_gets_beam &amp;&amp; !prev_note_gets_beam &amp;&amp; !next_note_gets_beam;</pre></div>
        
      
        
        <p>const beam_alone = note_gets_beam &amp;&amp; !prev_note_gets_beam &amp;&amp; !next_note_gets_beam;</p>

        
          <div class='highlight'><pre>      <span class="hljs-keyword">if</span> (note_gets_beam) {</pre></div>
        
      
        
        <p>This note gets a beam at the current level</p>

        
          <div class='highlight'><pre>        <span class="hljs-keyword">if</span> (beam_started) {</pre></div>
        
      
        
        <p>We’re currently in the middle of a beam. Just continue it on to
 the stem X of the current note.</p>

        
          <div class='highlight'><pre>          current_beam = beam_lines[beam_lines.length - <span class="hljs-number">1</span>];
          current_beam.end = stem_x;</pre></div>
        
      
        
        <p>If a secondary beam break is set up, end the beam right now.</p>

        
          <div class='highlight'><pre>          <span class="hljs-keyword">if</span> (should_break) {
            beam_started = <span class="hljs-literal">false</span>;
            <span class="hljs-keyword">if</span> (next_note &amp;&amp; !next_note_gets_beam &amp;&amp; current_beam.end === <span class="hljs-literal">null</span>) {</pre></div>
        
      
        
        <p>This note gets a beam,.but the next one does not. This means
 we need a partial pointing right.</p>

        
          <div class='highlight'><pre>              current_beam.end = current_beam.start - partial_beam_length;
            }
          }
        } <span class="hljs-keyword">else</span> {</pre></div>
        
      
        
        <p>No beam started yet. Start a new one.</p>

        
          <div class='highlight'><pre>          current_beam = { <span class="hljs-attr">start</span>: stem_x, <span class="hljs-attr">end</span>: <span class="hljs-literal">null</span> };
          beam_started = <span class="hljs-literal">true</span>;

          <span class="hljs-keyword">if</span> (beam_alone) {</pre></div>
        
      
        
        <p>previous and next beam exists and does not get a beam but current gets it.</p>

        
          <div class='highlight'><pre>            <span class="hljs-keyword">const</span> prev_tick = prev_note.getIntrinsicTicks();
            <span class="hljs-keyword">const</span> next_tick = next_note.getIntrinsicTicks();
            <span class="hljs-keyword">const</span> tick = note.getIntrinsicTicks();
            <span class="hljs-keyword">const</span> beam_direction = <span class="hljs-keyword">this</span>.lookupBeamDirection(duration, prev_tick, tick, next_tick);

            <span class="hljs-keyword">if</span> ([BEAM_LEFT, BEAM_BOTH].includes(beam_direction)) {
              current_beam.end = current_beam.start - partial_beam_length;
            } <span class="hljs-keyword">else</span> {
              current_beam.end = current_beam.start + partial_beam_length;
            }
          } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!next_note_gets_beam) {</pre></div>
        
      
        
        <p>The next note doesn’t get a beam. Draw a partial.</p>

        
          <div class='highlight'><pre>            <span class="hljs-keyword">if</span> ((previous_should_break || i === <span class="hljs-number">0</span>) &amp;&amp; next_note) {</pre></div>
        
      
        
        <p>This is the first note (but not the last one), or it is
 following a secondary break. Draw a partial to the right.</p>

        
          <div class='highlight'><pre>              current_beam.end = current_beam.start + partial_beam_length;
            } <span class="hljs-keyword">else</span> {</pre></div>
        
      
        
        <p>By default, draw a partial to the left.</p>

        
          <div class='highlight'><pre>              current_beam.end = current_beam.start - partial_beam_length;
            }
          } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (should_break) {</pre></div>
        
      
        
        <p>This note should have a secondary break after it. Even though
 we just started a beam, it needs to end immediately.</p>

        
          <div class='highlight'><pre>            current_beam.end = current_beam.start - partial_beam_length;
            beam_started = <span class="hljs-literal">false</span>;
          }
          beam_lines.push(current_beam);
        }
      } <span class="hljs-keyword">else</span> {</pre></div>
        
      
        
        <p>The current note does not get a beam.</p>

        
          <div class='highlight'><pre>        beam_started = <span class="hljs-literal">false</span>;
      }</pre></div>
        
      
        
        <p>Store the secondary break flag to inform the partial beam logic in
 the next iteration of the loop.</p>

        
          <div class='highlight'><pre>      previous_should_break = should_break;
    }</pre></div>
        
      
        
        <p>Add a partial beam pointing left if this is the last note in the group</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">const</span> last_beam = beam_lines[beam_lines.length - <span class="hljs-number">1</span>];
    <span class="hljs-keyword">if</span> (last_beam &amp;&amp; last_beam.end === <span class="hljs-literal">null</span>) {
      last_beam.end = last_beam.start - partial_beam_length;
    }
    <span class="hljs-keyword">return</span> beam_lines;
  }</pre></div>
        
      
        
        <p>Render the stems for each notes</p>

        
          <div class='highlight'><pre>  drawStems() {
    <span class="hljs-keyword">this</span>.notes.forEach(<span class="hljs-function"><span class="hljs-params">note</span> =&gt;</span> {
      <span class="hljs-keyword">if</span> (note.getStem()) {
        note.getStem().setContext(<span class="hljs-keyword">this</span>.context).draw();
      }
    }, <span class="hljs-keyword">this</span>);
  }</pre></div>
        
      
        
        <p>Render the beam lines</p>

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

    <span class="hljs-keyword">const</span> valid_beam_durations = [<span class="hljs-string">'4'</span>, <span class="hljs-string">'8'</span>, <span class="hljs-string">'16'</span>, <span class="hljs-string">'32'</span>, <span class="hljs-string">'64'</span>];

    <span class="hljs-keyword">const</span> firstNote = <span class="hljs-keyword">this</span>.notes[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">let</span> beamY = <span class="hljs-keyword">this</span>.getBeamYToDraw();
    <span class="hljs-keyword">const</span> firstStemX = firstNote.getStemX();
    <span class="hljs-keyword">const</span> beamThickness = <span class="hljs-keyword">this</span>.render_options.beam_width * <span class="hljs-keyword">this</span>.stem_direction;</pre></div>
        
      
        
        <p>Draw the beams.</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; valid_beam_durations.length; ++i) {
      <span class="hljs-keyword">const</span> duration = valid_beam_durations[i];
      <span class="hljs-keyword">const</span> beamLines = <span class="hljs-keyword">this</span>.getBeamLines(duration);

      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> j = <span class="hljs-number">0</span>; j &lt; beamLines.length; ++j) {
        <span class="hljs-keyword">const</span> beam_line = beamLines[j];
        <span class="hljs-keyword">const</span> startBeamX = beam_line.start;

        <span class="hljs-keyword">const</span> startBeamY = <span class="hljs-keyword">this</span>.getSlopeY(startBeamX, firstStemX, beamY, <span class="hljs-keyword">this</span>.slope);
        <span class="hljs-keyword">const</span> lastBeamX = beam_line.end;
        <span class="hljs-keyword">const</span> lastBeamY = <span class="hljs-keyword">this</span>.getSlopeY(lastBeamX, firstStemX, beamY, <span class="hljs-keyword">this</span>.slope);

        <span class="hljs-keyword">this</span>.context.beginPath();
        <span class="hljs-keyword">this</span>.context.moveTo(startBeamX, startBeamY);
        <span class="hljs-keyword">this</span>.context.lineTo(startBeamX, startBeamY + beamThickness);
        <span class="hljs-keyword">this</span>.context.lineTo(lastBeamX + <span class="hljs-number">1</span>, lastBeamY + beamThickness);
        <span class="hljs-keyword">this</span>.context.lineTo(lastBeamX + <span class="hljs-number">1</span>, lastBeamY);
        <span class="hljs-keyword">this</span>.context.closePath();
        <span class="hljs-keyword">this</span>.context.fill();
      }

      beamY += beamThickness * <span class="hljs-number">1.5</span>;
    }
  }</pre></div>
        
      
        
        <p>Pre-format the beam</p>

        
          <div class='highlight'><pre>  preFormat() { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>; }</pre></div>
        
      
        
        <p>Post-format the beam. This can only be called after
the notes in the beam have both <code>x</code> and <code>y</code> values. ie: they’ve
been formatted and have staves</p>

        
          <div class='highlight'><pre>  postFormat() {
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.postFormatted) <span class="hljs-keyword">return</span>;</pre></div>
        
      
        
        <p>Calculate a smart slope if we’re not forcing the beams to be flat.</p>

        
          <div class='highlight'><pre>    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.notes[<span class="hljs-number">0</span>].getCategory() === <span class="hljs-string">'tabnotes'</span> || <span class="hljs-keyword">this</span>.render_options.flat_beams) {
      <span class="hljs-keyword">this</span>.calculateFlatSlope();
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">this</span>.calculateSlope();
    }
    <span class="hljs-keyword">this</span>.applyStemExtensions();

    <span class="hljs-keyword">this</span>.postFormatted = <span class="hljs-literal">true</span>;
  }</pre></div>
        
      
        
        <p>Render the beam to the canvas context</p>

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

    <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>.postFormatted) {
      <span class="hljs-keyword">this</span>.postFormat();
    }

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