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.
- package/RELEASE.md +20 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +275 -36
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/package.json +1 -1
- package/src/write/creation/abstract-engraver.js +5 -4
- package/src/write/creation/create-note-head.js +6 -5
- package/src/write/creation/elements/relative-element.js +3 -0
- package/src/write/draw/absolute.js +6 -0
- package/src/write/draw/triplet.js +3 -3
- package/src/write/layout/beam.js +40 -10
- package/src/write/layout/layout.js +90 -0
- package/src/write/layout/set-upper-and-lower-elements.js +18 -2
- package/src/write/layout/to-time-and-staff-based.js +35 -0
- package/src/write/layout/triplet.js +61 -14
- package/version.js +1 -1
package/dist/abcjs-basic.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
26363
|
-
auxBeamEndX =
|
|
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
|
|
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
|
-
|
|
27086
|
-
|
|
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
|
-
|
|
27090
|
-
|
|
27091
|
-
|
|
27092
|
-
|
|
27093
|
-
|
|
27094
|
-
|
|
27095
|
-
|
|
27096
|
-
element.startNote
|
|
27097
|
-
|
|
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
|
-
|
|
27101
|
-
|
|
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.
|
|
28193
|
+
var version = '6.6.3';
|
|
27955
28194
|
module.exports = version;
|
|
27956
28195
|
|
|
27957
28196
|
/***/ })
|