File size: 5,951 Bytes
af6912c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
var layoutVoiceElements = require('./voice-elements');
function checkLastBarX(voices) {
var maxX = 0;
for (var i = 0; i < voices.length; i++) {
var curVoice = voices[i];
if (curVoice.children.length > 0) {
var lastChild = curVoice.children.length - 1;
var maxChild = curVoice.children[lastChild];
if (maxChild.abcelem.el_type === 'bar') {
var barX = maxChild.children[0].x;
if (barX > maxX) {
maxX = barX;
} else {
maxChild.children[0].x = maxX;
}
}
}
}
}
var layoutStaffGroup = function (spacing, minPadding, debug, staffGroup, leftEdge) {
var epsilon = 0.0000001; // Fudging for inexactness of floating point math.
var spacingunits = 0; // number of times we will have ended up using the spacing distance (as opposed to fixed width distances)
var minspace = 1000; // a big number to start off with - used to find out what the smallest space between two notes is -- GD 2014.1.7
var x = leftEdge;
staffGroup.startx = x;
var i;
var currentduration = 0;
if (debug) console.log("init layout", spacing);
for (i = 0; i < staffGroup.voices.length; i++) {
layoutVoiceElements.beginLayout(x, staffGroup.voices[i]);
}
var spacingunit = 0; // number of spacingunits coming from the previously laid out element to this one
while (!finished(staffGroup.voices)) {
// find first duration level to be laid out among candidates across voices
currentduration = null; // candidate smallest duration level
for (i = 0; i < staffGroup.voices.length; i++) {
if (!layoutVoiceElements.layoutEnded(staffGroup.voices[i]) && (!currentduration || getDurationIndex(staffGroup.voices[i]) < currentduration))
currentduration = getDurationIndex(staffGroup.voices[i]);
}
// isolate voices at current duration level
var currentvoices = [];
var othervoices = [];
for (i = 0; i < staffGroup.voices.length; i++) {
var durationIndex = getDurationIndex(staffGroup.voices[i]);
// PER: Because of the inexactness of JS floating point math, we just get close.
if (durationIndex - currentduration > epsilon) {
othervoices.push(staffGroup.voices[i]);
//console.log("out: voice ",i);
} else {
currentvoices.push(staffGroup.voices[i]);
//if (debug) console.log("in: voice ",i);
}
}
// among the current duration level find the one which needs starting furthest right
spacingunit = 0; // number of spacingunits coming from the previously laid out element to this one
var spacingduration = 0;
for (i = 0; i < currentvoices.length; i++) {
//console.log("greatest spacing unit", x, layoutVoiceElements.getNextX(currentvoices[i]), layoutVoiceElements.getSpacingUnits(currentvoices[i]), currentvoices[i].spacingduration);
if (layoutVoiceElements.getNextX(currentvoices[i]) > x) {
x = layoutVoiceElements.getNextX(currentvoices[i]);
spacingunit = layoutVoiceElements.getSpacingUnits(currentvoices[i]);
spacingduration = currentvoices[i].spacingduration;
}
}
spacingunits += spacingunit;
minspace = Math.min(minspace, spacingunit);
if (debug) console.log("currentduration: ", currentduration, spacingunits, minspace);
var lastTopVoice = undefined;
for (i = 0; i < currentvoices.length; i++) {
var v = currentvoices[i];
if (v.voicenumber === 0)
lastTopVoice = i;
var topVoice = (lastTopVoice !== undefined && currentvoices[lastTopVoice].voicenumber !== v.voicenumber) ? currentvoices[lastTopVoice] : undefined;
if (!isSameStaff(v, topVoice))
topVoice = undefined;
var voicechildx = layoutVoiceElements.layoutOneItem(x, spacing, v, minPadding, topVoice);
var dx = voicechildx - x;
if (dx > 0) {
x = voicechildx; //update x
for (var j = 0; j < i; j++) { // shift over all previously laid out elements
layoutVoiceElements.shiftRight(dx, currentvoices[j]);
}
}
}
// remove the value of already counted spacing units in other voices (e.g. if a voice had planned to use up 5 spacing units but is not in line to be laid out at this duration level - where we've used 2 spacing units - then we must use up 3 spacing units, not 5)
for (i = 0; i < othervoices.length; i++) {
othervoices[i].spacingduration -= spacingduration;
layoutVoiceElements.updateNextX(x, spacing, othervoices[i]); // adjust other voices expectations
}
// update indexes of currently laid out elems
for (i = 0; i < currentvoices.length; i++) {
var voice = currentvoices[i];
layoutVoiceElements.updateIndices(voice);
}
} // finished laying out
// find the greatest remaining x as a base for the width
for (i = 0; i < staffGroup.voices.length; i++) {
if (layoutVoiceElements.getNextX(staffGroup.voices[i]) > x) {
x = layoutVoiceElements.getNextX(staffGroup.voices[i]);
spacingunit = layoutVoiceElements.getSpacingUnits(staffGroup.voices[i]);
}
}
// adjust lastBar when needed (multi staves)
checkLastBarX(staffGroup.voices);
//console.log("greatest remaining",spacingunit,x);
spacingunits += spacingunit;
staffGroup.setWidth(x);
return { spacingUnits: spacingunits, minSpace: minspace };
};
function finished(voices) {
for (var i = 0; i < voices.length; i++) {
if (!layoutVoiceElements.layoutEnded(voices[i])) return false;
}
return true;
}
function getDurationIndex(element) {
return element.durationindex - (element.children[element.i] && (element.children[element.i].duration > 0) ? 0 : 0.0000005); // if the ith element doesn't have a duration (is not a note), its duration index is fractionally before. This enables CLEF KEYSIG TIMESIG PART, etc. to be laid out before we get to the first note of other voices
}
function isSameStaff(voice1, voice2) {
if (!voice1 || !voice1.staff || !voice1.staff.voices || voice1.staff.voices.length === 0)
return false;
if (!voice2 || !voice2.staff || !voice2.staff.voices || voice2.staff.voices.length === 0)
return false;
return (voice1.staff.voices[0] === voice2.staff.voices[0]);
}
module.exports = layoutStaffGroup;
|