package com.xenoage.zong.musiclayout.notator.chord.stem.single;
import static com.xenoage.utils.kernel.Range.range;
import static com.xenoage.utils.math.Fraction._1;
import com.xenoage.zong.core.music.MusicContext;
import com.xenoage.zong.core.music.chord.Chord;
import com.xenoage.zong.core.music.chord.Stem;
import com.xenoage.zong.core.music.chord.StemDirection;
import com.xenoage.zong.musiclayout.notation.chord.ChordLps;
/**
* Computes the {@link StemDirection} of single chords.
*
* @author Andreas Wenger
*/
public final class SingleStemDirector {
public static SingleStemDirector singleStemDirector = new SingleStemDirector();
public StemDirection compute(Chord chord, MusicContext context) {
//stem specified in chord?
Stem stem = chord.getStem();
if (stem.getDirection() != StemDirection.Default)
return stem.getDirection();
//stem needed?
if (!isStemNeeded(chord))
return StemDirection.None;
//compute default stem
ChordLps chordLp = new ChordLps(chord, context);
int linesCount = context.getLinesCount();
return compute(chordLp, linesCount);
}
public StemDirection compute(ChordLps chordLps, int staffLinesCount) {
//compute distance to middle line of staff
int distanceLowest = Math.abs(getDistanceToMidline(chordLps.getBottom(), staffLinesCount));
int distanceHighest = Math.abs(getDistanceToMidline(chordLps.getTop(), staffLinesCount));
//the side with the longest distance determines the direction
if (distanceLowest > distanceHighest) {
return StemDirection.Up;
}
else if (distanceLowest < distanceHighest) {
return StemDirection.Down;
}
//the distance is equal on both sides
//if the majority of notes on the stem are above the middleline,
//a down stem is used, otherwise an up-stem
int above = 0;
int below = 0;
for (int i : range(chordLps.getNotesCount())) {
int d = getDistanceToMidline(chordLps.get(i), staffLinesCount);
if (d > 0)
above++;
else if (d < 0)
below++;
}
if (below > above)
return StemDirection.Up;
else
return StemDirection.Down;
}
private boolean isStemNeeded(Chord chord) {
//all chords shorter than 1/1 need a stem
return chord.getDuration().isLessThan(_1);
}
private int getDistanceToMidline(int lp, int staffLinesCount) {
int middleLp = staffLinesCount - 1;
return lp - middleLp;
}
}