package com.xenoage.zong.musiclayout.notator.chord.stem.beam.range;
import static com.xenoage.utils.kernel.Range.range;
import static com.xenoage.zong.core.music.chord.StemDirection.Down;
import static com.xenoage.zong.core.music.chord.StemDirection.Up;
import static com.xenoage.zong.core.music.util.Interval.Before;
import static com.xenoage.zong.core.music.util.Interval.BeforeOrAt;
import static com.xenoage.zong.musiclayout.notator.chord.stem.single.SingleStemDirector.singleStemDirector;
import com.xenoage.zong.core.Score;
import com.xenoage.zong.core.music.MusicContext;
import com.xenoage.zong.core.music.beam.Beam;
import com.xenoage.zong.core.music.chord.Chord;
import com.xenoage.zong.core.music.chord.StemDirection;
import com.xenoage.zong.core.position.MP;
import com.xenoage.zong.musiclayout.notation.chord.ChordLps;
/**
* {@link Strategy} for a {@link Beam}, which spans over a single staff and measure.
*
* @author Andreas Wenger
*/
public class OneMeasureOneStaff
implements Strategy {
public static final OneMeasureOneStaff oneMeasureOneStaff = new OneMeasureOneStaff();
@Override public StemDirection[] compute(Beam beam, Score score) {
int staffLinesCount = getStaffLinesCount(beam.getChord(0), score);
ChordLps[] chordsLps = new ChordLps[beam.size()];
for (int iChord : range(chordsLps)) {
Chord chord = beam.getChord(iChord);
MP mp = MP.getMP(chord);
MusicContext mc = score.getMusicContext(mp, BeforeOrAt, Before);
chordsLps[iChord] = new ChordLps(chord, mc);
}
return compute(chordsLps, staffLinesCount);
}
public StemDirection[] compute(ChordLps[] chordsLps, int staffLinesCount) {
int staffMiddleLp = staffLinesCount - 1;
int upCount = 0;
int downCount = 0;
int furthestDistance = 0;
StemDirection furthestDistanceDir = Up;
//compute preferred stem directions and remember the stem direction of
//the chord with the note furthest away from the middle staff line
for (ChordLps chordLps : chordsLps) {
StemDirection preferredDir = singleStemDirector.compute(chordLps, staffLinesCount);
int distance;
if (preferredDir == Up) {
upCount++;
distance = staffMiddleLp - chordLps.getTop();
}
else {
downCount++;
distance = chordLps.getBottom() - staffMiddleLp;
}
if (distance > furthestDistance || //new furthest distance found
//equal furthest distance found, but Down wins (Ross, p. 95):
(distance == furthestDistance && preferredDir == Down)) {
furthestDistance = distance;
furthestDistanceDir = preferredDir;
}
}
//the mostly used stem direction wins (Ross, p. 95)
//if both directions are equally distributed, the stem direction of
//the chord with the note furthest away from the staff middle line wins (Ross, p. 95)
StemDirection finalStemDir;
if (upCount != downCount)
finalStemDir = (upCount > downCount ? Up : Down);
else
finalStemDir = furthestDistanceDir;
//use same direction for all stems
StemDirection[] dirs = new StemDirection[chordsLps.length];
for (int i : range(dirs))
dirs[i] = finalStemDir;
return dirs;
}
private int getStaffLinesCount(Chord chord, Score score) {
MP mp = MP.getMP(chord);
return score.getStaff(mp).getLinesCount();
}
}