package com.xenoage.zong.musiclayout.spacer.beat;
import static com.xenoage.utils.collections.CollectionUtils.alist;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import com.xenoage.utils.math.Fraction;
import com.xenoage.zong.core.header.ColumnHeader;
import com.xenoage.zong.core.music.barline.Barline;
import com.xenoage.zong.core.music.barline.BarlineRepeat;
import com.xenoage.zong.core.music.util.BeatE;
import com.xenoage.zong.musiclayout.spacing.BeatOffset;
/**
* Computes {@link BeatOffset}s for the barlines and the notes of the given measure column,
* based on the given {@link BeatOffset}s (that were created without respect to barlines).
*
* @author Andreas Wenger
*/
public class BarlinesBeatOffsetter {
public static final BarlinesBeatOffsetter barlinesBeatOffsetter = new BarlinesBeatOffsetter();
//additional 1 IS when using a repeat sign - TIDY: move into layout settings
static final float repeatSpace = 1;
//2 IS after a mid-measure barline - TIDY: move into layout settings
static final float midBarlineSpace = 2;
@AllArgsConstructor
public class Result {
public List<BeatOffset> voiceElementOffsets;
public List<BeatOffset> barlineOffsets;
}
public Result compute(List<BeatOffset> baseOffsets, ColumnHeader columnHeader, float maxInterlineSpace) {
ArrayList<BeatOffset> retNotes = alist(baseOffsets);
ArrayList<BeatOffset> retBarlines = alist();
//start barline
retBarlines.add(new BeatOffset(Fraction._0, 0));
Barline startBarline = columnHeader.getStartBarline();
if (startBarline != null && startBarline.getRepeat() == BarlineRepeat.Forward) {
//forward repeat: move all beats REPEAT_SPACE IS backward
float move = repeatSpace * maxInterlineSpace;
for (int i = 0; i < retNotes.size(); i++) {
BeatOffset oldOffset = retNotes.get(i);
retNotes.set(i, new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
}
}
//mid-measure barlines
for (BeatE<Barline> midBarline : columnHeader.getMiddleBarlines()) {
//get beat of barline, find it in the note offsets and move the following ones
Fraction beat = midBarline.beat;
int i = 0;
float move = 0;
for (; i < retNotes.size(); i++) {
if (retNotes.get(i).getBeat().compareTo(beat) >= 0) {
BarlineRepeat repeat = midBarline.element.getRepeat();
if (repeat == BarlineRepeat.Backward) {
//backward repeat: additional space before barline
move += repeatSpace * maxInterlineSpace;
BeatOffset oldOffset = retNotes.get(i);
retBarlines.add(new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
}
else if (repeat == BarlineRepeat.Forward) {
//forward repeat: additional space after barline
BeatOffset oldOffset = retNotes.get(i);
retBarlines.add(new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
move += repeatSpace * maxInterlineSpace;
}
else if (repeat == BarlineRepeat.Both) {
//forward and backward repeat: additional space before and after barline
move += repeatSpace * maxInterlineSpace;
BeatOffset oldOffset = retNotes.get(i);
retBarlines.add(new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
move += repeatSpace * maxInterlineSpace;
}
else {
retBarlines.add(retNotes.get(i));
}
move += midBarlineSpace * maxInterlineSpace;
break;
}
}
for (; i < retNotes.size(); i++) {
//move following notes
BeatOffset oldOffset = retNotes.get(i);
retNotes.set(i, new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
}
}
//end barline
BeatOffset lastOffset = retNotes.get(retNotes.size() - 1);
Barline endBarline = columnHeader.getEndBarline();
if (endBarline != null && endBarline.getRepeat() == BarlineRepeat.Backward) {
//backward repeat: additional space before end barline
float move = repeatSpace * maxInterlineSpace;
retBarlines.add(new BeatOffset(lastOffset.getBeat(), lastOffset.getOffsetMm() + move));
}
else {
retBarlines.add(lastOffset);
}
//return result
retBarlines.trimToSize();
return new Result(retNotes, retBarlines);
}
}