UNPKG

abcjs

Version:

Renderer for abc music notation

123 lines (112 loc) 5.21 kB
var getBarYAt = require('./get-bar-y-at'); function layoutTriplet(element) { // TODO end and beginning of line (PER: P.S. I'm not sure this can happen: I think the parser will always specify both the start and end points.) if (element.anchor1 && element.anchor2) { element.hasBeam = !!element.anchor1.parent.beam && element.anchor1.parent.beam === element.anchor2.parent.beam; var beam = element.anchor1.parent.beam; // if hasBeam is true, then the first and last element in the triplet have the same beam. // We also need to check if the beam doesn't contain other notes so that `(3 dcdcc` will do a bracket. if (element.hasBeam && (beam.elems[0] !== element.anchor1.parent || beam.elems[beam.elems.length - 1] !== element.anchor2.parent)) element.hasBeam = false; if (element.hasBeam) { // If there is a beam then we don't need to draw anything except the text. The beam could either be above or below. var left = isAbove(beam) ? element.anchor1.x + element.anchor1.w : element.anchor1.x; element.yTextPos = heightAtMidpoint(left, element.anchor2.x, beam); element.yTextPos += isAbove(beam) ? 3 : -2; // This creates some space between the beam and the number. element.xTextPos = xAtMidpoint(left, element.anchor2.x); element.top = element.yTextPos + 1; element.bottom = element.yTextPos - 2; if (isAbove(beam)) element.endingHeightAbove = 4; } else { // 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. // Above: // The bracket is never lower than the 'a' line, but is 4 pitches above the first and last notes. If there is // a tall note in the middle, the bracket is horizontal and above the highest note. // Below: The bracket is never higher than the 'C' line, and is 4 pitches below. // 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. var up = stemDirectionUp(element) element.up = up element.startNote = up ? Math.max(element.anchor1.parent.top, 9) + 4 : Math.min(element.anchor1.parent.bottom, 0) - 2 element.endNote = up ? Math.max(element.anchor2.parent.top, 9) + 4 : Math.min(element.anchor2.parent.bottom, 0) - 2 // If it starts or ends on a rest, make the beam horizontal 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; if (up) { // See if the middle note is really high. var max = 0; for (var i = 0; i < element.middleElems.length; i++) { max = Math.max(max, element.middleElems[i].top); } max += 4; if (max > element.startNote || max > element.endNote) { element.startNote = max + 3; element.endNote = max + 3; } } else { // See if the middle note is really low. var min = 0; for (var i = 0; i < element.middleElems.length; i++) { min = Math.min(min, element.middleElems[i].bottom-element.middleElems[i].height); } min -= 3; if (min < element.startNote && min < element.endNote) { element.startNote = Math.min(min, element.startNote) - 2; element.endNote = Math.min(min, element.endNote) - 2; } } if (element.flatBeams) { if (up) { element.startNote = Math.max(element.startNote, element.endNote); element.endNote = Math.max(element.startNote, element.endNote); } else { element.startNote = Math.min(element.startNote, element.endNote); element.endNote = Math.min(element.startNote, element.endNote); } } element.yTextPos = element.startNote + (element.endNote - element.startNote) / 2; element.xTextPos = element.anchor1.x + (element.anchor2.x + element.anchor2.w - element.anchor1.x) / 2; element.top = element.yTextPos + 1; element.bottom = element.yTextPos - 2; } } delete element.middleElems; delete element.flatBeams; } function isAbove(beam) { return beam.stemsUp; } function stemDirectionUp(element) { var up = 0 var down = 0 if (element.anchor1) { if (element.anchor1.stemDir === 'up') up++ if (element.anchor1.stemDir === 'down') down++ } if (element.anchor2) { if (element.anchor2.stemDir === 'up') up++ if (element.anchor2.stemDir === 'down') down++ } if (element.middleElems) { for (var i = 0; i < element.middleElems.length; i++) { var elem = element.middleElems[i] if (elem.stemDir === 'up') up++ if (elem.stemDir === 'down') down++ } } return up >= down } // 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 function heightAtMidpoint(startX, endX, beam) { if (beam.beams.length === 0) return 0; beam = beam.beams[0]; var midPoint = startX + (endX - startX) / 2; return getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, midPoint); } function xAtMidpoint(startX, endX) { return startX + (endX - startX) / 2; } module.exports = layoutTriplet;