abcjs 6.6.2 → 6.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19014,6 +19014,7 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
19014
19014
  }
19015
19015
  }
19016
19016
  var hasStem = !nostem && durlog <= -1;
19017
+ var chordPos = pp > 1 ? p + 1 : null;
19017
19018
  var ret = createNoteHead(abselem, c, elem.pitches[p], {
19018
19019
  dir: dir,
19019
19020
  extrax: -roomTaken,
@@ -19023,7 +19024,8 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
19023
19024
  scale: this.voiceScale,
19024
19025
  accidentalSlot: accidentalSlot,
19025
19026
  shouldExtendStem: !stemdir,
19026
- printAccidentals: !voice.isPercussion
19027
+ printAccidentals: !voice.isPercussion,
19028
+ chordPos: chordPos
19027
19029
  });
19028
19030
  symbolWidth = Math.max(glyphs.getSymbolWidth(c), symbolWidth);
19029
19031
  abselem.extraw -= ret.extraLeft;
@@ -19074,7 +19076,7 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
19074
19076
  symbolWidth: symbolWidth
19075
19077
  };
19076
19078
  };
19077
- AbstractEngraver.prototype.addLyric = function (abselem, elem) {
19079
+ AbstractEngraver.prototype.addLyric = function (abselem, elem, voiceNumber) {
19078
19080
  var lyricStr = "";
19079
19081
  elem.lyric.forEach(function (ly) {
19080
19082
  var div = ly.divider === ' ' ? "" : ly.divider;
@@ -19086,7 +19088,8 @@ AbstractEngraver.prototype.addLyric = function (abselem, elem) {
19086
19088
  type: "lyric",
19087
19089
  position: position,
19088
19090
  height: lyricDim.height / spacing.STEP,
19089
- dim: this.getTextSize.attr('vocalfont', "lyric")
19091
+ dim: this.getTextSize.attr('vocalfont', "lyric"),
19092
+ voiceNumber: voiceNumber
19090
19093
  }));
19091
19094
  };
19092
19095
  AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaff, voice) {
@@ -19138,7 +19141,7 @@ AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaf
19138
19141
  symbolWidth = ret2.symbolWidth;
19139
19142
  }
19140
19143
  if (elem.lyric !== undefined) {
19141
- this.addLyric(abselem, elem);
19144
+ this.addLyric(abselem, elem, voice.voicenumber);
19142
19145
  }
19143
19146
  if (elem.gracenotes !== undefined) {
19144
19147
  roomtaken += this.addGraceNotes(elem, voice, abselem, notehead, this.stemHeight * this.voiceScale, this.isBagpipes, roomtaken);
@@ -19784,6 +19787,7 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19784
19787
  var accidentalSlot = options.accidentalSlot !== undefined ? options.accidentalSlot : [];
19785
19788
  var shouldExtendStem = options.shouldExtendStem !== undefined ? options.shouldExtendStem : false;
19786
19789
  var printAccidentals = options.printAccidentals !== undefined ? options.printAccidentals : true;
19790
+ var chordPos = options.chordPos;
19787
19791
 
19788
19792
  // TODO scale the dot as well
19789
19793
  var pitch = pitchelem.verticalPos;
@@ -19794,7 +19798,9 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19794
19798
  if (c === undefined) abselem.addFixed(new RelativeElement("pitch is undefined", 0, 0, 0, {
19795
19799
  type: "debug"
19796
19800
  }));else if (c === "") {
19797
- notehead = new RelativeElement(null, 0, 0, pitch);
19801
+ notehead = new RelativeElement(null, 0, 0, pitch, {
19802
+ chordPos: chordPos
19803
+ });
19798
19804
  } else {
19799
19805
  var shiftheadx = headx;
19800
19806
  if (pitchelem.printer_shift) {
@@ -19805,7 +19811,8 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19805
19811
  scalex: scale,
19806
19812
  scaley: scale,
19807
19813
  thickness: glyphs.symbolHeightInPitches(c) * scale,
19808
- name: pitchelem.name
19814
+ name: pitchelem.name,
19815
+ chordPos: chordPos
19809
19816
  };
19810
19817
  notehead = new RelativeElement(c, shiftheadx, glyphs.getSymbolWidth(c) * scale, pitch, opts);
19811
19818
  notehead.stemDir = dir;
@@ -19820,13 +19827,16 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19820
19827
  var xdelta = dir === "down" ? headx : headx + notehead.w - 0.6;
19821
19828
  abselem.addRight(new RelativeElement(flag, xdelta, glyphs.getSymbolWidth(flag) * scale, pos, {
19822
19829
  scalex: scale,
19823
- scaley: scale
19830
+ scaley: scale,
19831
+ chordPos: chordPos
19824
19832
  }));
19825
19833
  }
19826
19834
  newDotShiftX = notehead.w + dotshiftx - 2 + 5 * dot;
19827
19835
  for (; dot > 0; dot--) {
19828
19836
  var dotadjusty = 1 - Math.abs(pitch) % 2; //PER: take abs value of the pitch. And the shift still happens on ledger lines.
19829
- abselem.addRight(new RelativeElement("dots.dot", notehead.w + dotshiftx - 2 + 5 * dot, glyphs.getSymbolWidth("dots.dot"), pitch + dotadjusty));
19837
+ abselem.addRight(new RelativeElement("dots.dot", notehead.w + dotshiftx - 2 + 5 * dot, glyphs.getSymbolWidth("dots.dot"), pitch + dotadjusty, {
19838
+ chordPos: chordPos
19839
+ }));
19830
19840
  }
19831
19841
  }
19832
19842
  if (notehead) notehead.highestVert = pitchelem.highestVert;
@@ -19875,7 +19885,8 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
19875
19885
  scalex: scale,
19876
19886
  scaley: scale,
19877
19887
  top: pitch + h / 2,
19878
- bottom: pitch - h / 2
19888
+ bottom: pitch - h / 2,
19889
+ chordPos: chordPos
19879
19890
  }));
19880
19891
  extraLeft = glyphs.getSymbolWidth(symb) / 2; // TODO-PER: We need a little extra width if there is an accidental, but I'm not sure why it isn't the full width of the accidental.
19881
19892
  }
@@ -21112,6 +21123,7 @@ var RelativeElement = function RelativeElement(c, dx, w, pitch, opt) {
21112
21123
  this.pitch2 = opt.pitch2;
21113
21124
  this.linewidth = opt.linewidth;
21114
21125
  this.klass = opt.klass;
21126
+ this.chordPos = opt.chordPos;
21115
21127
  this.anchor = opt.anchor ? opt.anchor : 'middle';
21116
21128
  this.top = pitch;
21117
21129
  if (this.pitch2 !== undefined && this.pitch2 > this.top) this.top = this.pitch2;
@@ -21126,6 +21138,7 @@ var RelativeElement = function RelativeElement(c, dx, w, pitch, opt) {
21126
21138
  }
21127
21139
  if (opt.dim) this.dim = opt.dim;
21128
21140
  if (opt.position) this.position = opt.position;
21141
+ if (opt.voiceNumber !== undefined) this.voiceNumber = opt.voiceNumber;
21129
21142
  this.height = opt.height ? opt.height : 4; // The +1 is to give a little bit of padding.
21130
21143
  if (opt.top) this.top = opt.top;
21131
21144
  if (opt.bottom) this.bottom = opt.bottom;
@@ -22679,6 +22692,11 @@ function drawAbsolute(renderer, params, bartop, selectables, staffPos) {
22679
22692
  if (child.type === "symbol" && child.c && child.c.indexOf('notehead') >= 0) {
22680
22693
  el.setAttribute('class', 'abcjs-notehead');
22681
22694
  }
22695
+ if (el && child.chordPos && child.name.indexOf('flags.') !== 0) {
22696
+ var klass = el.getAttribute("class");
22697
+ if (klass) klass = klass + ' abcjs-chord-pos-' + child.chordPos;else klass = 'abcjs-chord-pos-' + child.chordPos;
22698
+ el.setAttribute('class', klass);
22699
+ }
22682
22700
  }
22683
22701
  }
22684
22702
  var klass = params.type;
@@ -24842,7 +24860,7 @@ function drawTriplet(renderer, params, selectables) {
24842
24860
  "data-name": "triplet"
24843
24861
  });
24844
24862
  if (!params.hasBeam) {
24845
- drawBracket(renderer, params.anchor1.x, params.startNote, params.anchor2.x + params.anchor2.w, params.endNote);
24863
+ drawBracket(renderer, params.anchor1.x, params.startNote, params.anchor2.x + params.anchor2.w, params.endNote, params.up);
24846
24864
  }
24847
24865
  // HACK: adjust the position of "3". It is too high in all cases so we fudge it by subtracting 1 here.
24848
24866
  renderText(renderer, {
@@ -24866,10 +24884,10 @@ function drawTriplet(renderer, params, selectables) {
24866
24884
  function drawLine(l, t, r, b) {
24867
24885
  return sprintf("M %f %f L %f %f", roundNumber(l), roundNumber(t), roundNumber(r), roundNumber(b));
24868
24886
  }
24869
- function drawBracket(renderer, x1, y1, x2, y2) {
24887
+ function drawBracket(renderer, x1, y1, x2, y2, up) {
24870
24888
  y1 = renderer.calcY(y1);
24871
24889
  y2 = renderer.calcY(y2);
24872
- var bracketHeight = 5;
24890
+ var bracketHeight = up ? 5 : -5;
24873
24891
 
24874
24892
  // Draw vertical lines at the beginning and end
24875
24893
  var pathString = "";
@@ -26592,6 +26610,7 @@ var setUpperAndLowerElements = __webpack_require__(/*! ./set-upper-and-lower-ele
26592
26610
  var layoutStaffGroup = __webpack_require__(/*! ./staff-group */ "./src/write/layout/staff-group.js");
26593
26611
  var getLeftEdgeOfStaff = __webpack_require__(/*! ./get-left-edge-of-staff */ "./src/write/layout/get-left-edge-of-staff.js");
26594
26612
  var layoutInGrid = __webpack_require__(/*! ./layout-in-grid */ "./src/write/layout/layout-in-grid.js");
26613
+ var toTimeAndStaffBased = __webpack_require__(/*! ./to-time-and-staff-based */ "./src/write/layout/to-time-and-staff-based.js");
26595
26614
 
26596
26615
  // This sets the "x" attribute on all the children in abctune.lines
26597
26616
  // It also sets the "w" and "startx" attributes on "voices"
@@ -26627,6 +26646,16 @@ var layout = function layout(renderer, abctune, width, space, expandToWidest, ti
26627
26646
  }
26628
26647
  }
26629
26648
 
26649
+ // See if there are collisions between voices that need to be tweaked
26650
+ var timeBased = toTimeAndStaffBased(abctune.lines);
26651
+ for (i = 0; i < abctune.lines.length; i++) {
26652
+ abcLine = abctune.lines[i];
26653
+ if (abcLine.staffGroup) {
26654
+ fixVoiceCollisions(timeBased[i]);
26655
+ //setUpperAndLowerElements(renderer, abcLine.staffGroup);
26656
+ }
26657
+ }
26658
+
26630
26659
  // Set the staff spacing
26631
26660
  // TODO-PER: we should have been able to do this by the time we called setUpperAndLowerElements, but for some reason the "bottom" element seems to be set as a side effect of setting the X spacing.
26632
26661
  for (i = 0; i < abctune.lines.length; i++) {
@@ -26709,6 +26738,82 @@ function centerWholeRests(voices) {
26709
26738
  }
26710
26739
  }
26711
26740
  }
26741
+ function fixVoiceCollisions(timeBasedLine) {
26742
+ for (var s = 0; s < timeBasedLine.length; s++) {
26743
+ var timeSlot = timeBasedLine[s];
26744
+ // If there is more than one thing happening at the same time,
26745
+ // and one of those things is a rest, then:
26746
+ // If the rest is in the first element, check to see if the bottom bumps into the top of any of the rest of the elements
26747
+ // If the rest is in the last element, check to see if the top bumps into the bottom of any of the rest of the elements.
26748
+ // Note: if there are more than two voices the staff will get sloppy, so there is a limit to how much that can be improved, but this should be fine when there are two voices.
26749
+ // If there is a collision, move the rest up or down to fix that.
26750
+ var keys = Object.keys(timeSlot);
26751
+ for (var z = 0; z < keys.length; z++) {
26752
+ var slot = timeSlot[keys[z]]; // slot is an array of all the things happening at a particular time
26753
+ var lastIndex = slot.length - 1;
26754
+ if (slot.length > 1) {
26755
+ var isRealRest = slot[0].abcelem.rest && slot[0].abcelem.rest.type === 'rest'; // weed out invisible rests
26756
+ var isRealRest2 = slot[lastIndex].abcelem.rest && slot[lastIndex].abcelem.rest.type === 'rest'; // weed out invisible rests
26757
+ if (isRealRest && !slot[lastIndex].abcelem.rest) {
26758
+ // the first voice has a rest and the second doesn't
26759
+ var restTop = slot[0].children.find(function (ch) {
26760
+ return ch.name.includes('rest');
26761
+ });
26762
+ var otherTop = closeTop(slot[lastIndex]);
26763
+ if (restTop) {
26764
+ var distance1 = restTop.bottom - otherTop;
26765
+ distance1 -= 2; // give some room between the rest and the note
26766
+ if (distance1 < 0 && slot[0].children.length > 0) {
26767
+ slot[0].bottom -= distance1;
26768
+ slot[0].top -= distance1;
26769
+ slot[0].children[0].bottom -= distance1;
26770
+ slot[0].children[0].top -= distance1;
26771
+ slot[0].children[0].pitch -= distance1;
26772
+ }
26773
+ }
26774
+ } else if (isRealRest2 && !slot[0].abcelem.rest) {
26775
+ // the last voice has a rest and the first doesn't
26776
+ var restBottom = slot[lastIndex].children.find(function (ch) {
26777
+ return ch.name.includes('rest');
26778
+ });
26779
+ if (restBottom) {
26780
+ var distance2 = restBottom.top - closeBottom(slot[0]);
26781
+ distance2 += 2; // give some room between the rest and the note
26782
+ if (distance2 > 0 && slot[lastIndex].children.length > 0) {
26783
+ slot[lastIndex].bottom -= distance2;
26784
+ slot[lastIndex].top -= distance2;
26785
+ slot[lastIndex].children[0].bottom -= distance2;
26786
+ slot[lastIndex].children[0].top -= distance2;
26787
+ slot[lastIndex].children[0].pitch -= distance2;
26788
+ }
26789
+ }
26790
+ }
26791
+ }
26792
+ }
26793
+ }
26794
+ }
26795
+ function closeTop(absElem) {
26796
+ if (absElem.children) {
26797
+ var max = -90; // This is clearly way lower than the max calculated below
26798
+ for (var i = 0; i < absElem.children.length; i++) {
26799
+ var child = absElem.children[i];
26800
+ if (child.type !== 'chord') max = Math.max(max, child.top);
26801
+ }
26802
+ if (max > -90) return max;
26803
+ }
26804
+ return absElem.top;
26805
+ }
26806
+ function closeBottom(absElem) {
26807
+ if (absElem.children) {
26808
+ var min = 90; // This is clearly way higher than the min calculated below
26809
+ for (var i = 0; i < absElem.children.length; i++) {
26810
+ var child = absElem.children[i];
26811
+ if (child.type !== 'lyric') min = Math.min(min, child.bottom);
26812
+ }
26813
+ if (min < 90) return min;
26814
+ }
26815
+ return absElem.bottom;
26816
+ }
26712
26817
  module.exports = layout;
26713
26818
 
26714
26819
  /***/ }),
@@ -26788,7 +26893,8 @@ var setUpperAndLowerElements = function setUpperAndLowerElements(renderer, staff
26788
26893
 
26789
26894
  for (var j = 0; j < staff.voices.length; j++) {
26790
26895
  var voice = staffGroup.voices[staff.voices[j]];
26791
- setUpperAndLowerVoiceElements(positionY, voice, renderer.spacing);
26896
+ var diff = setUpperAndLowerVoiceElements(positionY, voice, renderer.spacing);
26897
+ staff.bottom -= diff; //
26792
26898
  }
26793
26899
  // We might need a little space in between staves if the staves haven't been pushed far enough apart by notes or extra vertical stuff.
26794
26900
  // Only try to put in extra space if this isn't the top staff.
@@ -26821,10 +26927,18 @@ function incTop(staff, positionY, item, count) {
26821
26927
  function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
26822
26928
  var i;
26823
26929
  var abselem;
26930
+ var diff = 0;
26824
26931
  for (i = 0; i < voice.children.length; i++) {
26825
26932
  abselem = voice.children[i];
26826
- setUpperAndLowerAbsoluteElements(positionY, abselem, spacing);
26933
+ var bottom = setUpperAndLowerAbsoluteElements(positionY, abselem, spacing);
26934
+ if (bottom < abselem.bottom) {
26935
+ // We're moving things down so tell the staff that it needs to be taller
26936
+ diff = abselem.bottom - bottom;
26937
+ abselem.bottom = bottom; //
26938
+ voice.bottom = bottom; //
26939
+ }
26827
26940
  }
26941
+
26828
26942
  for (i = 0; i < voice.otherchildren.length; i++) {
26829
26943
  abselem = voice.otherchildren[i];
26830
26944
  switch (abselem.type) {
@@ -26847,6 +26961,7 @@ function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
26847
26961
  break;
26848
26962
  }
26849
26963
  }
26964
+ return diff;
26850
26965
  }
26851
26966
 
26852
26967
  // For each of the relative elements that can't be placed in advance (because their vertical placement depends on everything
@@ -26854,6 +26969,7 @@ function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
26854
26969
  // hash with the vertical placement (in pitch units) for each type.
26855
26970
  // TODO-PER: I think this needs to be separated by "above" and "below". How do we know that for dynamics at the point where they are being defined, though? We need a pass through all the relative elements to set "above" and "below".
26856
26971
  function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
26972
+ var bottom = element.bottom;
26857
26973
  // specialYResolved contains the actual pitch for each of the classes of elements.
26858
26974
  for (var i = 0; i < element.children.length; i++) {
26859
26975
  var child = element.children[i];
@@ -26863,6 +26979,12 @@ function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
26863
26979
  if (child[key]) {
26864
26980
  // If this relative element has defined a height for this class of element
26865
26981
  child.pitch = specialYResolved[key];
26982
+ if (key === 'lyricHeightBelow' && child.type === 'lyric' && child.voiceNumber) {
26983
+ // TODO-PER: This can result in extra unused vertical space if there are lyrics only on the second but not the first voice.
26984
+ child.pitch -= child.voiceNumber * child[key]; //
26985
+ bottom = Math.min(element.bottom, child.pitch); //
26986
+ }
26987
+
26866
26988
  if (child.top === undefined) {
26867
26989
  // TODO-PER: HACK! Not sure this is the right place to do this.
26868
26990
  if (child.type === 'TempoElement') {
@@ -26877,6 +26999,7 @@ function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
26877
26999
  }
26878
27000
  }
26879
27001
  }
27002
+ return bottom;
26880
27003
  }
26881
27004
  function setUpperAndLowerCrescendoElements(positionY, element) {
26882
27005
  if (element.dynamicHeightAbove) element.pitch = positionY.dynamicHeightAbove;else element.pitch = positionY.dynamicHeightBelow;
@@ -27083,6 +27206,47 @@ module.exports = layoutStaffGroup;
27083
27206
 
27084
27207
  /***/ }),
27085
27208
 
27209
+ /***/ "./src/write/layout/to-time-and-staff-based.js":
27210
+ /*!*****************************************************!*\
27211
+ !*** ./src/write/layout/to-time-and-staff-based.js ***!
27212
+ \*****************************************************/
27213
+ /***/ (function(module) {
27214
+
27215
+ function toTimeAndStaffBased(abcLines) {
27216
+ var results = [];
27217
+ for (var lin = 0; lin < abcLines.length; lin++) {
27218
+ var line = abcLines[lin];
27219
+ var staffGroup = line.staffGroup;
27220
+ var group = [];
27221
+ if (staffGroup && staffGroup && staffGroup.staffs) {
27222
+ for (var s = 0; s < staffGroup.staffs.length; s++) {
27223
+ var staff = staffGroup.staffs[s];
27224
+ var timeSlot = {};
27225
+ for (var i = 0; i < staff.voices.length; i++) {
27226
+ var voice = staffGroup.voices[staff.voices[i]];
27227
+ var time = 0;
27228
+ for (var k = 0; k < voice.children.length; k++) {
27229
+ var index = 'T' + Math.round(time * 1000); // There can be inexactness when calculating triplets, so we'll round, but we'll make sure that no make sure that we don't lose necessary precision by making it a shorter time than would ever happen
27230
+ if (!timeSlot[index]) timeSlot[index] = [];
27231
+ if (voice.children[k].abcelem.el_type === 'note') {
27232
+ timeSlot[index].push(voice.children[k]);
27233
+ time += voice.children[k].duration;
27234
+ }
27235
+ }
27236
+ }
27237
+ // Now timeSlot is an object with all the voices on a particular staff that
27238
+ // happen at the same time as an array.
27239
+ group.push(timeSlot);
27240
+ }
27241
+ }
27242
+ results.push(group);
27243
+ }
27244
+ return results;
27245
+ }
27246
+ module.exports = toTimeAndStaffBased;
27247
+
27248
+ /***/ }),
27249
+
27086
27250
  /***/ "./src/write/layout/triplet.js":
27087
27251
  /*!*************************************!*\
27088
27252
  !*** ./src/write/layout/triplet.js ***!
@@ -27108,30 +27272,56 @@ function layoutTriplet(element) {
27108
27272
  element.bottom = element.yTextPos - 2;
27109
27273
  if (isAbove(beam)) element.endingHeightAbove = 4;
27110
27274
  } else {
27111
- // If there isn't a beam, then we need to draw the bracket and the text. The bracket is always above.
27275
+ // If there isn't a beam, then we need to draw the bracket and the text. The bracket is either above or below depending on the stem direction of the notes.
27276
+ // Above:
27112
27277
  // The bracket is never lower than the 'a' line, but is 4 pitches above the first and last notes. If there is
27113
27278
  // a tall note in the middle, the bracket is horizontal and above the highest note.
27114
- element.startNote = Math.max(element.anchor1.parent.top, 9) + 4;
27115
- element.endNote = Math.max(element.anchor2.parent.top, 9) + 4;
27279
+ // Below: The bracket is never higher than the 'C' line, and is 4 pitches below.
27280
+
27281
+ // To decide if the bracket goes above or below, go in the direction of the most stems. If there are the same number it will put the bracket above.
27282
+ var up = stemDirectionUp(element);
27283
+ element.up = up;
27284
+ element.startNote = up ? Math.max(element.anchor1.parent.top, 9) + 4 : Math.min(element.anchor1.parent.bottom, 0) - 2;
27285
+ element.endNote = up ? Math.max(element.anchor2.parent.top, 9) + 4 : Math.min(element.anchor2.parent.bottom, 0) - 2;
27286
+
27116
27287
  // If it starts or ends on a rest, make the beam horizontal
27117
27288
  if (element.anchor1.parent.type === "rest" && element.anchor2.parent.type !== "rest") element.startNote = element.endNote;else if (element.anchor2.parent.type === "rest" && element.anchor1.parent.type !== "rest") element.endNote = element.startNote;
27118
- // See if the middle note is really high.
27119
- var max = 0;
27120
- for (var i = 0; i < element.middleElems.length; i++) {
27121
- max = Math.max(max, element.middleElems[i].top);
27122
- }
27123
- max += 4;
27124
- if (max > element.startNote || max > element.endNote) {
27125
- element.startNote = max;
27126
- element.endNote = max;
27289
+ if (up) {
27290
+ // See if the middle note is really high.
27291
+ var max = 0;
27292
+ for (var i = 0; i < element.middleElems.length; i++) {
27293
+ max = Math.max(max, element.middleElems[i].top);
27294
+ }
27295
+ max += 4;
27296
+ if (max > element.startNote || max > element.endNote) {
27297
+ element.startNote = max + 3;
27298
+ element.endNote = max + 3;
27299
+ }
27300
+ } else {
27301
+ // See if the middle note is really low.
27302
+ var min = 0;
27303
+ for (var i = 0; i < element.middleElems.length; i++) {
27304
+ min = Math.min(min, element.middleElems[i].bottom - element.middleElems[i].height);
27305
+ }
27306
+ min -= 3;
27307
+ if (min < element.startNote && min < element.endNote) {
27308
+ element.startNote = Math.min(min, element.startNote) - 2;
27309
+ element.endNote = Math.min(min, element.endNote) - 2;
27310
+ }
27127
27311
  }
27128
27312
  if (element.flatBeams) {
27129
- element.startNote = Math.max(element.startNote, element.endNote);
27130
- element.endNote = Math.max(element.startNote, element.endNote);
27313
+ if (up) {
27314
+ element.startNote = Math.max(element.startNote, element.endNote);
27315
+ element.endNote = Math.max(element.startNote, element.endNote);
27316
+ } else {
27317
+ element.startNote = Math.min(element.startNote, element.endNote);
27318
+ element.endNote = Math.min(element.startNote, element.endNote);
27319
+ }
27131
27320
  }
27132
27321
  element.yTextPos = element.startNote + (element.endNote - element.startNote) / 2;
27133
27322
  element.xTextPos = element.anchor1.x + (element.anchor2.x + element.anchor2.w - element.anchor1.x) / 2;
27134
27323
  element.top = element.yTextPos + 1;
27324
+ element.bottom = element.yTextPos - 2;
27135
27325
  }
27136
27326
  }
27137
27327
  delete element.middleElems;
@@ -27140,6 +27330,26 @@ function layoutTriplet(element) {
27140
27330
  function isAbove(beam) {
27141
27331
  return beam.stemsUp;
27142
27332
  }
27333
+ function stemDirectionUp(element) {
27334
+ var up = 0;
27335
+ var down = 0;
27336
+ if (element.anchor1) {
27337
+ if (element.anchor1.stemDir === 'up') up++;
27338
+ if (element.anchor1.stemDir === 'down') down++;
27339
+ }
27340
+ if (element.anchor2) {
27341
+ if (element.anchor2.stemDir === 'up') up++;
27342
+ if (element.anchor2.stemDir === 'down') down++;
27343
+ }
27344
+ if (element.middleElems) {
27345
+ for (var i = 0; i < element.middleElems.length; i++) {
27346
+ var elem = element.middleElems[i];
27347
+ if (elem.stemDir === 'up') up++;
27348
+ if (elem.stemDir === 'down') down++;
27349
+ }
27350
+ }
27351
+ return up >= down;
27352
+ }
27143
27353
 
27144
27354
  // We can't just use the entire beam for the calculation. The range has to be passed in, because the beam might extend into some unrelated notes. for instance, (3_a'f'e'f'2 when L:16
27145
27355
  function heightAtMidpoint(startX, endX, beam) {
@@ -27980,7 +28190,7 @@ module.exports = Svg;
27980
28190
  \********************/
27981
28191
  /***/ (function(module) {
27982
28192
 
27983
- var version = '6.6.2';
28193
+ var version = '6.6.3';
27984
28194
  module.exports = version;
27985
28195
 
27986
28196
  /***/ })