abcjs 6.6.1 → 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 = "";
@@ -26306,6 +26324,27 @@ function createStems(elems, asc, beam, dy, mainNote) {
26306
26324
  parent.addRight(stem);
26307
26325
  }
26308
26326
  }
26327
+
26328
+ // Helper function to find the next non-rest element in the array
26329
+ function findNextNonRest(elems, startIndex) {
26330
+ for (var k = startIndex + 1; k < elems.length; k++) {
26331
+ if (!elems[k].abcelem.rest) {
26332
+ return k;
26333
+ }
26334
+ }
26335
+ return -1; // No non-rest element found
26336
+ }
26337
+
26338
+ // Helper function to find the previous non-rest element in the array
26339
+ function findPrevNonRest(elems, startIndex) {
26340
+ for (var k = startIndex - 1; k >= 0; k--) {
26341
+ if (!elems[k].abcelem.rest) {
26342
+ return k;
26343
+ }
26344
+ }
26345
+ return -1; // No non-rest element found
26346
+ }
26347
+
26309
26348
  function createAdditionalBeams(elems, asc, beam, isGrace, dy) {
26310
26349
  var beams = [];
26311
26350
  var auxBeams = []; // auxbeam will be {x, y, durlog, single} auxbeam[0] should match with durlog=-4 (16th) (j=-4-durlog)
@@ -26343,24 +26382,32 @@ function createAdditionalBeams(elems, asc, beam, isGrace, dy) {
26343
26382
  }
26344
26383
  }
26345
26384
  for (var j = auxBeams.length - 1; j >= 0; j--) {
26346
- if (i === elems.length - 1 || getDurlog(elems[i + 1].abcelem.duration) > -j - 4) {
26385
+ // Find the next non-rest element to check if we should end the beam
26386
+ var nextNonRestIndex = findNextNonRest(elems, i);
26387
+ var shouldEndBeam = nextNonRestIndex === -1 || nextNonRestIndex < elems.length && getDurlog(elems[nextNonRestIndex].abcelem.duration) > -j - 4;
26388
+ if (shouldEndBeam) {
26347
26389
  var auxBeamEndX = x;
26348
26390
  var auxBeamEndY = bary + sy * (j + 1);
26349
26391
  if (auxBeams[j].single) {
26350
- if (i === 0) {
26392
+ var prevNonRestIndex = findPrevNonRest(elems, i);
26393
+ var isFirstNote = prevNonRestIndex === -1;
26394
+ var isLastNote = nextNonRestIndex === -1;
26395
+ if (isFirstNote) {
26351
26396
  // This is the first note in the group, always draw the beam to the right
26352
26397
  auxBeamEndX = x + 5;
26353
- } else if (i === elems.length - 1) {
26398
+ } else if (isLastNote) {
26354
26399
  // This is the last note in the group, always draw the beam to the left
26355
26400
  auxBeamEndX = x - 5;
26356
26401
  } else {
26357
- // This is a middle note, check the note durations of the notes to the left and right
26358
- if (elems[i - 1].duration === elems[i + 1].duration) {
26402
+ // This is a middle note, check the note durations of the notes to the left and right (skipping rests)
26403
+ var prevDuration = elems[prevNonRestIndex].abcelem.duration;
26404
+ var nextDuration = elems[nextNonRestIndex].abcelem.duration;
26405
+ if (prevDuration === nextDuration) {
26359
26406
  // The notes on either side are the same duration, alternate which side the beam goes to
26360
26407
  auxBeamEndX = i % 2 === 0 ? x + 5 : x - 5;
26361
26408
  } else {
26362
- // The notes on either side are different durations, draw the beam to the shorter note
26363
- auxBeamEndX = elems[i - 1].duration > elems[i + 1].duration ? x + 5 : x - 5;
26409
+ // The notes on either side are different durations, draw the beam to the longer note
26410
+ auxBeamEndX = prevDuration < nextDuration ? x + 5 : x - 5;
26364
26411
  }
26365
26412
  }
26366
26413
  auxBeamEndY = getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, auxBeamEndX) + sy * (j + 1);
@@ -26563,6 +26610,7 @@ var setUpperAndLowerElements = __webpack_require__(/*! ./set-upper-and-lower-ele
26563
26610
  var layoutStaffGroup = __webpack_require__(/*! ./staff-group */ "./src/write/layout/staff-group.js");
26564
26611
  var getLeftEdgeOfStaff = __webpack_require__(/*! ./get-left-edge-of-staff */ "./src/write/layout/get-left-edge-of-staff.js");
26565
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");
26566
26614
 
26567
26615
  // This sets the "x" attribute on all the children in abctune.lines
26568
26616
  // It also sets the "w" and "startx" attributes on "voices"
@@ -26598,6 +26646,16 @@ var layout = function layout(renderer, abctune, width, space, expandToWidest, ti
26598
26646
  }
26599
26647
  }
26600
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
+
26601
26659
  // Set the staff spacing
26602
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.
26603
26661
  for (i = 0; i < abctune.lines.length; i++) {
@@ -26680,6 +26738,82 @@ function centerWholeRests(voices) {
26680
26738
  }
26681
26739
  }
26682
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
+ }
26683
26817
  module.exports = layout;
26684
26818
 
26685
26819
  /***/ }),
@@ -26759,7 +26893,8 @@ var setUpperAndLowerElements = function setUpperAndLowerElements(renderer, staff
26759
26893
 
26760
26894
  for (var j = 0; j < staff.voices.length; j++) {
26761
26895
  var voice = staffGroup.voices[staff.voices[j]];
26762
- setUpperAndLowerVoiceElements(positionY, voice, renderer.spacing);
26896
+ var diff = setUpperAndLowerVoiceElements(positionY, voice, renderer.spacing);
26897
+ staff.bottom -= diff; //
26763
26898
  }
26764
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.
26765
26900
  // Only try to put in extra space if this isn't the top staff.
@@ -26792,10 +26927,18 @@ function incTop(staff, positionY, item, count) {
26792
26927
  function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
26793
26928
  var i;
26794
26929
  var abselem;
26930
+ var diff = 0;
26795
26931
  for (i = 0; i < voice.children.length; i++) {
26796
26932
  abselem = voice.children[i];
26797
- 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
+ }
26798
26940
  }
26941
+
26799
26942
  for (i = 0; i < voice.otherchildren.length; i++) {
26800
26943
  abselem = voice.otherchildren[i];
26801
26944
  switch (abselem.type) {
@@ -26818,6 +26961,7 @@ function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
26818
26961
  break;
26819
26962
  }
26820
26963
  }
26964
+ return diff;
26821
26965
  }
26822
26966
 
26823
26967
  // For each of the relative elements that can't be placed in advance (because their vertical placement depends on everything
@@ -26825,6 +26969,7 @@ function setUpperAndLowerVoiceElements(positionY, voice, spacing) {
26825
26969
  // hash with the vertical placement (in pitch units) for each type.
26826
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".
26827
26971
  function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
26972
+ var bottom = element.bottom;
26828
26973
  // specialYResolved contains the actual pitch for each of the classes of elements.
26829
26974
  for (var i = 0; i < element.children.length; i++) {
26830
26975
  var child = element.children[i];
@@ -26834,6 +26979,12 @@ function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
26834
26979
  if (child[key]) {
26835
26980
  // If this relative element has defined a height for this class of element
26836
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
+
26837
26988
  if (child.top === undefined) {
26838
26989
  // TODO-PER: HACK! Not sure this is the right place to do this.
26839
26990
  if (child.type === 'TempoElement') {
@@ -26848,6 +26999,7 @@ function setUpperAndLowerAbsoluteElements(specialYResolved, element, spacing) {
26848
26999
  }
26849
27000
  }
26850
27001
  }
27002
+ return bottom;
26851
27003
  }
26852
27004
  function setUpperAndLowerCrescendoElements(positionY, element) {
26853
27005
  if (element.dynamicHeightAbove) element.pitch = positionY.dynamicHeightAbove;else element.pitch = positionY.dynamicHeightBelow;
@@ -27054,6 +27206,47 @@ module.exports = layoutStaffGroup;
27054
27206
 
27055
27207
  /***/ }),
27056
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
+
27057
27250
  /***/ "./src/write/layout/triplet.js":
27058
27251
  /*!*************************************!*\
27059
27252
  !*** ./src/write/layout/triplet.js ***!
@@ -27079,30 +27272,56 @@ function layoutTriplet(element) {
27079
27272
  element.bottom = element.yTextPos - 2;
27080
27273
  if (isAbove(beam)) element.endingHeightAbove = 4;
27081
27274
  } else {
27082
- // 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:
27083
27277
  // The bracket is never lower than the 'a' line, but is 4 pitches above the first and last notes. If there is
27084
27278
  // a tall note in the middle, the bracket is horizontal and above the highest note.
27085
- element.startNote = Math.max(element.anchor1.parent.top, 9) + 4;
27086
- 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
+
27087
27287
  // If it starts or ends on a rest, make the beam horizontal
27088
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;
27089
- // See if the middle note is really high.
27090
- var max = 0;
27091
- for (var i = 0; i < element.middleElems.length; i++) {
27092
- max = Math.max(max, element.middleElems[i].top);
27093
- }
27094
- max += 4;
27095
- if (max > element.startNote || max > element.endNote) {
27096
- element.startNote = max;
27097
- 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
+ }
27098
27311
  }
27099
27312
  if (element.flatBeams) {
27100
- element.startNote = Math.max(element.startNote, element.endNote);
27101
- 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
+ }
27102
27320
  }
27103
27321
  element.yTextPos = element.startNote + (element.endNote - element.startNote) / 2;
27104
27322
  element.xTextPos = element.anchor1.x + (element.anchor2.x + element.anchor2.w - element.anchor1.x) / 2;
27105
27323
  element.top = element.yTextPos + 1;
27324
+ element.bottom = element.yTextPos - 2;
27106
27325
  }
27107
27326
  }
27108
27327
  delete element.middleElems;
@@ -27111,6 +27330,26 @@ function layoutTriplet(element) {
27111
27330
  function isAbove(beam) {
27112
27331
  return beam.stemsUp;
27113
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
+ }
27114
27353
 
27115
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
27116
27355
  function heightAtMidpoint(startX, endX, beam) {
@@ -27951,7 +28190,7 @@ module.exports = Svg;
27951
28190
  \********************/
27952
28191
  /***/ (function(module) {
27953
28192
 
27954
- var version = '6.6.1';
28193
+ var version = '6.6.3';
27955
28194
  module.exports = version;
27956
28195
 
27957
28196
  /***/ })