package com.xenoage.zong.musiclayout.notator.chord.stem; import com.xenoage.zong.core.music.StaffLines; import com.xenoage.zong.core.music.chord.StemDirection; import com.xenoage.zong.musiclayout.notation.chord.ChordLps; import static com.xenoage.zong.core.music.chord.StemDirection.Down; import static com.xenoage.zong.core.music.chord.StemDirection.Up; /** * Computes the lenght of a stem for a chord. * * The values are valid for a stem with no beam or just a single beam line (8th). * For beams with more lines, more space may be required in a later step. * * @author Andreas Wenger */ public class StemDrawer { public static final StemDrawer stemDrawer = new StemDrawer(); /** * Gets the ideal stem length in IS of the chord with the given notes * (ascending LPs) using the given stem direction within a staff with the * given number of lines. */ public float getPreferredStemLengthIs(ChordLps notesLp, StemDirection stemDir, StaffLines staff) { int bottomNoteLp = notesLp.getBottom(); int topNoteLp = notesLp.getTop(); //begin with the extreme LPs //supports also staves with more or less than 5 lines if (isStemExtendedToMiddleLine(notesLp, stemDir, staff)) { if (stemDir == Down) return (bottomNoteLp - staff.middleLp) / 2f; else return (staff.middleLp - topNoteLp) / 2f; } else if ((stemDir == Up && topNoteLp > staff.topLp - 4) || (stemDir == Down && bottomNoteLp <= staff.bottomLp + 2)) { //Ross, p. 86, row 3. Upstem notes above the third last staff line //or downstem notes at or below the second staff line are shorter //(generalized for staff with more than 5 lines) return 2.5f; } else if ((stemDir == Up && topNoteLp == staff.topLp - 4) || (stemDir == Down && bottomNoteLp == staff.bottomLp + 3)) { //Ross, p. 86, row 6. Special cases for upstem/downstem note //on "3rd last line"/"2nd space" //(generalized for staff with more than 5 lines) return 3f; } else { //default stem length. //Ross, p. 83. Also see footnote, which explains why e.g. Chlapik p. 39, 4, //recommends a length of 3 spaces (just a different measurement) return 3.5f; } } /** * Returns the minimum stem length which still looks artistically correct. */ public float getMinStemLength() { //Ross, p. 83 return 2.5f; } /** * Returns true, iff the given chord needs a stem which needs to be lengthened to * touch the middle line. */ public boolean isStemExtendedToMiddleLine(ChordLps notesLp, StemDirection stemDir, StaffLines staff) { //Ross, p. 86, last row. Also Chlapik p. 39, 5. //For downstem/upstem notes, which are above/below the first top/bottom //leger line position, lengthen the stem to the middle staff line. //Ross starts this rule at the second leger line, while Chlapik starts it //from the first leger line. In practice, this makes no difference, since //the default length of 3.5 spaces also matches that position. return (stemDir == Down && notesLp.getBottom() > staff.topLegerLp) || (stemDir == Up && notesLp.getTop() < staff.bottomLegerLp); } }