package com.xenoage.zong.musiclayout.layouter.scoreframelayout;
import com.xenoage.utils.StringUtils;
import com.xenoage.utils.collections.CList;
import com.xenoage.utils.collections.IList;
import com.xenoage.utils.iterators.It;
import com.xenoage.zong.core.music.lyric.Lyric;
import com.xenoage.zong.core.music.lyric.SyllableType;
import com.xenoage.zong.core.text.Alignment;
import com.xenoage.zong.core.text.FormattedText;
import com.xenoage.zong.core.text.FormattedTextStyle;
import com.xenoage.zong.musiclayout.stampings.NoteheadStamping;
import com.xenoage.zong.musiclayout.stampings.StaffStamping;
import com.xenoage.zong.musiclayout.stampings.StaffTextStamping;
import com.xenoage.zong.musiclayout.stampings.Stamping;
import java.util.List;
import static com.xenoage.utils.collections.CList.clist;
import static com.xenoage.zong.core.music.format.SP.sp;
import static com.xenoage.zong.core.text.FormattedText.fText;
import static com.xenoage.zong.core.text.FormattedTextUtils.styleText;
/**
* Creates the {@link Stamping}s of a lyric.
*
* @author Andreas Wenger
*/
public class LyricStamper {
public static final LyricStamper lyricStamper = new LyricStamper();
/**
* Creates and returns the {@link StaffTextStamping} for the given {@link Lyric},
* using the given text style, that belongs to the given parent staff.
* The horizontal position in mm and the vertical position (baseline)
* as a line position are also given.
* If it is a extend syllable, null is returned.
*/
public StaffTextStamping createSyllableStamping(Lyric lyric, FormattedTextStyle style,
StaffStamping staffStamping, float positionX, float baseLinePosition) {
if (lyric.getSyllableType() == SyllableType.Extend)
return null;
FormattedText text = styleText(lyric.getText(), style, Alignment.Center);
return new StaffTextStamping(text, sp(positionX, baseLinePosition), staffStamping, lyric);
}
/**
* Creates and returns the {@link StaffTextStamping} containing
* a hypen ("-") between the two given lyric stampings.
*/
public StaffTextStamping createHyphenStamping(StaffTextStamping syllableLeft,
StaffTextStamping syllableRight, FormattedTextStyle style) {
FormattedText text = fText("-", style, Alignment.Center);
float positionX;
float widthLeft = syllableLeft.getText().getFirstParagraph().getMetrics().getWidth();
if (syllableLeft.parentStaff == syllableRight.parentStaff) {
//syllables are in same staff
//the horizontal position of the hyphen is in the middle of the empty
//space between the left and right syllable
float widthRight = syllableRight.getText().getFirstParagraph().getMetrics().getWidth();
positionX = ((syllableLeft.position.xMm + widthLeft / 2) + (syllableRight.position.xMm - widthRight / 2)) / 2;
}
else {
//right syllable is in a new staff
//place the hypen to the right side of the first syllable
positionX = (syllableLeft.position.xMm + widthLeft / 2) + 2 *
text.getFirstParagraph().getMetrics().getWidth();
}
return new StaffTextStamping(text, sp(positionX, syllableLeft.position.lp),
syllableLeft.parentStaff, syllableLeft.getElement()); //hyphen belongs to the left syllable
}
/**
* Creates and returns at least one {@link StaffTextStamping} containing
* an underscore line ("___") behind the given lyric stamping
* up to the given notehead stamping.
* If more than one line is needed, a stamping for each line is returned.
*
* A list of consecutive staff stampings must be given, containing at least
* the beginning and the ending staff of the underscore. If the underscore needs
* only one staff, it may also be null.
*
* The left syllable must be given, even if it is not in the current frame.
* The right notehead is optional. If not given, the underscore is drawn over
* all given following staves.
*/
public IList<StaffTextStamping> createUnderscoreStampings(StaffTextStamping syllableLeft,
NoteheadStamping noteheadRight, FormattedTextStyle style, List<StaffStamping> staffStampings) {
if (syllableLeft == null)
throw new IllegalArgumentException("Left syllable must be given");
CList<StaffTextStamping> ret = clist();
//measure width of "_"
float widthU = fText("_", style, Alignment.Center).getFirstParagraph().getMetrics().getWidth();
//compute the horizontal start position, base line and element
float widthLeft = syllableLeft.getText().getFirstParagraph().getMetrics().getWidth();
float startX = syllableLeft.position.xMm + widthLeft / 2 + widthU / 4; //widthU / 4: just some distance
float baseLine = syllableLeft.position.lp;
Object element = syllableLeft.element;
//if end notehead is given, compute the end position
float endX = 0;
if (noteheadRight != null) {
endX = noteheadRight.position.xMm;
}
//underscore line on a single staff?
if (noteheadRight != null && syllableLeft.parentStaff == noteheadRight.parentStaff) {
//simple case
ret.add(createUnderscoreStamping(startX, endX, baseLine, widthU, style,
syllableLeft.parentStaff, element));
}
else {
It<StaffStamping> staves = new It<>(staffStampings);
StaffStamping currentStaff = null;
boolean firstStaffFound = false; //only true, when start stamping is found
boolean lastStaffFound = false; //only true, when stop stamping is found
//find the start staff
StaffStamping s1 = syllableLeft.parentStaff;
while (staves.hasNext()) {
currentStaff = staves.next();
if (currentStaff == s1) {
firstStaffFound = true;
break;
}
}
//if not found, begin at the very beginning
if (!firstStaffFound) {
staves = new It<>(staffStampings);
}
//first staff (if any): begin at the stamping, go to the end of the system
if (firstStaffFound) {
ret.add(createUnderscoreStamping(startX, currentStaff.positionMm.x + currentStaff.lengthMm,
baseLine, widthU, style, currentStaff, element));
}
//create curved lines in the middle staves, from the beginning (without leading spacing)
//to the end of the system
while (staves.hasNext()) {
currentStaff = staves.next();
if (noteheadRight != null && currentStaff == noteheadRight.parentStaff) {
//stop, because this is the last staff, where we have the stop notehead
lastStaffFound = true;
break;
}
//create underscore over whole staff
ret.add(createUnderscoreStamping(currentStaff.positionMm.x, currentStaff.positionMm.x +
currentStaff.lengthMm, baseLine, widthU, style, currentStaff, element));
}
//last staff (if any): begin at the beginning (without leading spacing) of the
//system, stop at the notehead
if (lastStaffFound) {
ret.add(createUnderscoreStamping(currentStaff.positionMm.x, noteheadRight.position.xMm,
baseLine, widthU, style, currentStaff, element));
}
}
return ret;
}
private StaffTextStamping createUnderscoreStamping(float startX, float endX, float baseLine,
float widthUnderscore, FormattedTextStyle style, StaffStamping staff, Object element) {
//compute number of needed "_"
int countU = Math.max((int) ((endX - startX) / widthUnderscore) + 1, 1);
//create text
FormattedText text = fText(StringUtils.repeat("_", countU), style, Alignment.Left);
return new StaffTextStamping(text, sp(startX, baseLine), staff, element);
}
}