package material.beam.slant;
import com.xenoage.utils.collections.ArrayUtils;
import com.xenoage.utils.collections.CList;
import com.xenoage.zong.core.music.chord.StemDirection;
import com.xenoage.zong.musiclayout.notation.chord.ChordLps;
import com.xenoage.zong.musiclayout.spacer.beam.placement.SingleStaffBeamPlacer.Placement;
import com.xenoage.zong.musiclayout.spacer.beam.stem.BeamedStem;
import com.xenoage.zong.musiclayout.spacer.beam.stem.BeamedStems;
import lombok.Getter;
import lombok.val;
import material.ExampleBase;
import material.Suite;
import java.util.List;
import static com.xenoage.utils.collections.CollectionUtils.alist;
import static com.xenoage.utils.kernel.Range.range;
import static com.xenoage.zong.core.music.StaffLines.staff5Lines;
import static com.xenoage.zong.musiclayout.SLP.slp;
import static com.xenoage.zong.musiclayout.notator.chord.stem.StemDrawer.stemDrawer;
import static com.xenoage.zong.musiclayout.notator.chord.stem.beam.range.OneMeasureOneStaff.oneMeasureOneStaff;
/**
* Beam slant example.
*
* @author Andreas Wenger
*/
public class Example
implements ExampleBase {
public static List<Suite<Example>> all = alist(new ChlapikBeamSlant(), new RossBeamSlant());
@Getter public String name;
public int leftNoteLp;
public int rightNoteLp;
public float leftStemLengthIs;
public float rightStemLengthIs;
public float widthIs = Float.NaN;
public int[] middleNotesLps = new int[0];
private StemDirection stemDir = StemDirection.Default;
public final int staffLines = 5;
public static Example example(String name) {
Example ret = new Example();
ret.name = name;
return ret;
}
/**
* Simple example with normal spacing, like on Ross, page 104.
*/
public static Example example(String name, int leftNoteLp, double leftStemLengthIs,
int rightNoteLp, double rightStemLengthIs) {
return example(name).left(leftNoteLp, leftStemLengthIs).width(4.5).right(
rightNoteLp, rightStemLengthIs);
}
public Example left(int noteLp, double stemLengthIs) {
leftNoteLp = noteLp;
leftStemLengthIs = (float) stemLengthIs;
return this;
}
public Example width(double widthIs) {
this.widthIs = (float) widthIs;
return this;
}
public Example middleNotes(int... noteLps) {
middleNotesLps = noteLps;
return this;
}
public Example right(int noteLp, double stemLengthIs) {
rightNoteLp = noteLp;
rightStemLengthIs = (float) stemLengthIs;
return this;
}
public Example stemDir(StemDirection stemDir) {
this.stemDir = stemDir;
return this;
}
public float[] getStemsXIs() {
//compute positions. use same distance for each note
float distanceIs = getStemsDistanceIs();
float[] stemsXIs = new float[2 + middleNotesLps.length];
for (int i : range(stemsXIs)) {
stemsXIs[i] = i * distanceIs;
}
return stemsXIs;
}
public float getStemsDistanceIs() {
if (Double.isNaN(widthIs))
return 5; //use normal distance of 5 spaces as default
else
return widthIs / (middleNotesLps.length + 1); //equally split width for all stems
}
public float getSlantIs() {
int stemDir = getStemDir().getSign();
float leftStemEndLp = leftNoteLp + stemDir * leftStemLengthIs * 2;
float rightStemEndLp = rightNoteLp + stemDir * rightStemLengthIs * 2;
return (rightStemEndLp - leftStemEndLp) / 2;
}
public int[] getNotesLp() {
int chordsCount = 2 + middleNotesLps.length;
int[] notesLp = new int[chordsCount];
notesLp[0] = leftNoteLp;
for (int i : range(middleNotesLps))
notesLp[1 + i] = middleNotesLps[i];
notesLp[chordsCount - 1] = rightNoteLp;
return notesLp;
}
public StemDirection getStemDir() {
if (stemDir != StemDirection.Default)
return stemDir;
//compute stem direction
int[] notesLp = getNotesLp();
ChordLps[] chordLps = new ChordLps[notesLp.length];
for (int i : range(notesLp))
chordLps[i] = new ChordLps(notesLp[i]);
return oneMeasureOneStaff.compute(chordLps, staffLines)[0];
}
public BeamedStems getStems() {
val stems = new CList<BeamedStem>();
int[] notesLp = getNotesLp();
val stemDir = getStemDir();
float distance = getStemsDistanceIs();
for (int i : range(notesLp)) {
float stemXIs = i * distance;
float stemLengthIs;
if (i == 0)
stemLengthIs = leftStemLengthIs;
else if (i == notesLp.length - 1)
stemLengthIs = rightStemLengthIs;
else
stemLengthIs = stemDrawer.getPreferredStemLengthIs(new ChordLps(notesLp[i]), stemDir, staff5Lines);
float stemEndLp = notesLp[i] + stemDir.getSign() * stemLengthIs * 2;
stems.add(new BeamedStem(stemXIs, stemDir, slp(0, notesLp[i]), slp(0, stemEndLp)));
}
return new BeamedStems(stems.close());
}
public Placement getPlacement() {
StemDirection stemDir = getStemDir();
return new Placement(
leftNoteLp + stemDir.getSign() * leftStemLengthIs * 2,
rightNoteLp + stemDir.getSign() * rightStemLengthIs * 2);
}
/**
* Returns an array of the preferred stem lengths.
* The stem length of the left and right chord is known, the other
* ones are simply set to 2.5.
*/
@Deprecated
public float[] getStemsLengthIs() {
int notesCount = getNotesLp().length;
float[] ret = ArrayUtils.arrayFloat(notesCount, 2.5f);
ret[0] = leftStemLengthIs;
ret[notesCount - 1] = rightStemLengthIs;
return ret;
}
}