package com.xenoage.zong.musiclayout.notator.chord;
import static com.xenoage.zong.core.music.annotation.ArticulationType.Staccato;
import static com.xenoage.zong.core.music.annotation.ArticulationType.Tenuto;
import java.util.List;
import com.xenoage.utils.math.VSide;
import com.xenoage.zong.core.music.annotation.Articulation;
import com.xenoage.zong.core.music.annotation.ArticulationType;
import com.xenoage.zong.core.music.chord.Chord;
import com.xenoage.zong.core.music.chord.StemDirection;
import com.xenoage.zong.musiclayout.notation.chord.ArticulationDisplacement;
import com.xenoage.zong.musiclayout.notation.chord.ArticulationsNotation;
import com.xenoage.zong.musiclayout.notation.chord.NoteDisplacement;
import com.xenoage.zong.musiclayout.notation.chord.NotesNotation;
/**
* Computes the notations of the articulations of a given chord.
*
* @author Andreas Wenger
*/
public class ArticulationsNotator {
public static final ArticulationsNotator articulationsNotator = new ArticulationsNotator();
/**
* Computes the notations of the articulations of the given chord.
* If there are no articulations, {@link ArticulationsNotation#empty} is returned.
*/
public ArticulationsNotation compute(Chord chord,
StemDirection stemDirection, NotesNotation notesAlignment, int linesCount) {
//depending on the stem direction, place the articulation on the other side.
//if there is no stem direction, always place at the top
VSide side = (stemDirection == StemDirection.Up ? VSide.Bottom : VSide.Top);
//dependent on the side of the articulation, take the top or bottom note
NoteDisplacement outerNote = (side == VSide.Top ? notesAlignment.getTopNote()
: notesAlignment.getBottomNote());
//compute alignment of articulations
return compute(chord.getArticulations(), outerNote, side, linesCount);
}
ArticulationsNotation compute(List<Articulation> articulations,
NoteDisplacement outerNote, VSide side, int staffLinesCount) {
if (articulations.size() == 0)
return ArticulationsNotation.empty;
//special cases (which appear often): if there is only a single articulation
//which is either a staccato or tenuto, we can place it between the staff lines
if (articulations.size() == 1 &&
(articulations.get(0).getType() == Staccato ||
articulations.get(0).getType() == Tenuto)) {
return computeSimpleArticulation(articulations.get(0).getType(), outerNote, side, staffLinesCount);
}
//otherwise, the articulations a placed above or below the staff
else {
return computeOtherArticulations(articulations, outerNote, side, staffLinesCount);
}
}
/**
* Creates an {@link ArticulationsNotation} for the given {@link ArticulationType}
* at the given notehead at the given side. If possible, it is placed between
* the staff lines.
*/
ArticulationsNotation computeSimpleArticulation(ArticulationType articulation,
NoteDisplacement outerNote, VSide side, int staffLinesCount) {
//compute LP of the articulation: if within staff, it must be
//between the staff lines (LP 1, 3, 5, ...)
int lp = outerNote.lp + 2 * side.getDir();
if (lp >= 0 && lp <= (staffLinesCount - 1) * 2 && //within staff
lp % 2 == 0) { //on staff line
lp += side.getDir(); //move one LP further
}
//compute ArticulationsAlignment
float height = 1; //1 IS
ArticulationDisplacement[] arts = { new ArticulationDisplacement(lp, outerNote.xIs, articulation) };
return new ArticulationsNotation(arts, height);
}
/**
* Creates an {@link ArticulationsNotation} for the given {@link ArticulationType}s
* at the given notehead at the given side. The articulations are always placed
* outside the staff lines. The first one is placed as the innermost articulation,
* the last one as the outermost one.
*/
ArticulationsNotation computeOtherArticulations(List<Articulation> articulations,
NoteDisplacement outerNote, VSide side, int staffLinesCount) {
//compute LP of the first articulation:
//if within staff, it must be moved outside
int lp = outerNote.lp + 2 * side.getDir();
if (lp >= 0 && lp <= (staffLinesCount - 1) * 2) //within staff
lp = (side == VSide.Top ? (staffLinesCount - 1) * 2 + 1 : -1);
//collect displacements
ArticulationDisplacement[] arts = new ArticulationDisplacement[articulations.size()];
for (int i = 0; i < articulations.size(); i++) {
arts[i] = new ArticulationDisplacement(lp + 2 * i * side.getDir(), outerNote.xIs,
articulations.get(i).getType());
}
//total height: 1 IS for each articulation
float heightIS = Math.abs(lp - outerNote.lp) / 2;
//create ArticulationsAlignment
return new ArticulationsNotation(arts, heightIS);
}
}