use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class AlignedVoicesSpacer method compute.
/**
* Modifies the given {@link VoiceSpacing}, based on the given beat offsets.
*/
public void compute(VoiceSpacing voiceSpacing, List<BeatOffset> beatOffsets) {
List<ElementSpacing> spacingElements = voiceSpacing.elements;
if (spacingElements.size() == 0 || beatOffsets.size() == 0)
return;
// find the given beats, that are also used here
List<BeatOffset> sharedBeats = computeSharedBeats(spacingElements, beatOffsets);
// interpolate positions between the shared beats
float lastGivenBeatPosition = 0;
float lastEndElementPosition = 0;
int firstElement = 0;
int lastElement = -1;
float interlineSpace = voiceSpacing.interlineSpace;
for (int iGivenBeat : range(sharedBeats)) {
// for each given beat: find elements before or at that beat
for (int iElement : range(lastElement + 1, spacingElements.size() - 1)) {
if (spacingElements.get(iElement).beat.compareTo(sharedBeats.get(iGivenBeat).getBeat()) > 0)
break;
lastElement++;
}
if (lastElement == -1)
break;
// compute horizontal positions and distances of the
// given beat offsets and the current voice spacing, from the
// last shared beat up to the current shared beat.
// we calculate in interline spaces here
float currentGivenBeatPosition = sharedBeats.get(iGivenBeat).getOffsetMm() / interlineSpace;
float givenBeatsDistance = currentGivenBeatPosition - lastGivenBeatPosition;
float currentEndElementPosition = spacingElements.get(lastElement).xIs;
float elementsDistance = currentEndElementPosition - lastEndElementPosition;
// interpolate the offsets of the current voice spacing
// between the last shared beat and the current shared beat.
// do this in reverse order, because the position of grace notes is dependent
// on the position of the (following) main note
float lastOriginalOffsetIs = getLast(beatOffsets).offsetMm / interlineSpace;
for (int iElement : rangeReverse(lastElement, firstElement)) {
ElementSpacing e = spacingElements.get(iElement);
if (false == e.isGrace()) {
// normal element: interpolate position
float currentElementOffset = e.xIs - lastEndElementPosition;
float newElementOffset;
if (elementsDistance != 0) {
// scale offset according to the given beats distance
newElementOffset = currentElementOffset / elementsDistance * givenBeatsDistance;
} else {
newElementOffset = currentGivenBeatPosition;
}
lastOriginalOffsetIs = e.xIs;
e.xIs = newElementOffset + lastGivenBeatPosition;
} else {
// grace element: same distance to the main note as before
float distanceBefore = lastOriginalOffsetIs - e.xIs;
lastOriginalOffsetIs = e.xIs;
e.xIs = spacingElements.get(iElement + 1).xIs - distanceBefore;
}
}
// next range up to next shared beat
firstElement = lastElement + 1;
if (firstElement >= spacingElements.size())
break;
lastGivenBeatPosition = currentGivenBeatPosition;
lastEndElementPosition = currentEndElementPosition;
}
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class VoiceStamper method stampVoice.
public List<Stamping> stampVoice(VoiceSpacing voice, float voiceXMm, StaffStampings staffStampings, boolean stampLeadingRests, StamperContext context, // TODO:
FormattedTextStyle defaultLyricStyle, Map<Beam, BeamSpacing> beams, OpenSlursCache openCurvedLinesCache, OpenLyricsCache openLyricsCache, LastLyrics lastLyrics, OpenTupletsCache openTupletsCache) {
List<Stamping> ret = alist();
// create the voice elements
boolean onlyRestsSoFar = true;
for (ElementSpacing spacingElement : voice.elements) {
MusicElement element = spacingElement.getElement();
if (element != null) /* TODO && (stampRests || !(element instanceof Rest)) */
{
Notation notation = context.getNotation(element);
float xMm = voiceXMm + spacingElement.xIs * voice.interlineSpace;
if (element instanceof Chord) {
// chord
onlyRestsSoFar = false;
Chord chord = (Chord) element;
BeamSpacing beam = beams.get(chord.getBeam());
ret.addAll(chordStamper.stampAll((ChordNotation) spacingElement.getNotation(), xMm, beam, staffStampings, context, defaultLyricStyle, openCurvedLinesCache, openLyricsCache, lastLyrics, openTupletsCache));
} else if (spacingElement instanceof RestSpacing) {
// rest
if (false == onlyRestsSoFar || stampLeadingRests) {
// not a leading rest, or a leading rest which should be stamped
ret.add(elementStamper.createRestStamping((RestSpacing) spacingElement, xMm, context));
}
} else {
throw new IllegalArgumentException("Notation not supported: " + notation);
}
}
}
return ret;
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class MeasureStamper method stampMeasure.
/**
* Stamps all {@link MeasureElement}s of the given measure (but not the leading elements).
* @param measureXMm the horizontal position on the staff in mm, where the measure
* starts (after leading spacing).
*/
public List<Stamping> stampMeasure(MeasureSpacing measure, float measureXMm, StamperContext context) {
List<Stamping> ret = alist();
for (ElementSpacing element : measure.elements) {
MusicElement me = element.getElement();
if (me != null) {
Notation notation = context.getNotation(me);
float xMm = measureXMm + element.xIs * measure.interlineSpace;
ret.add(stamp(notation, xMm, context));
}
}
return ret;
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class VoicesBeatOffsetter method computeMinimalDistance.
/**
* Computes and returns the minimal distance in mm
* within the given spacing elements of the given voice
* between the given starting and ending beat (ending beat
* without its width).
*
* Beats may be multiused. The last element with the given start beat
* and also the last element of the given end beat are used
* (because the important offset of a beat is the position of the main note
* or rest, not the position of a grace note or a clef or key signature).
*
* If both the starting and ending beat are used,
* computing their minimal distance is simple.
*
* If the ending beat is unused, 0 is returned, since the given
* voice does not need any space because it has no element to place there.
*
* If the starting beat is unused, we have to compute
* the distance in the following way:
*
* The following example shows 2 voices:
*
* # # # ? { #: there the offsets are already known and given
* 1/4 1/4 1/4 |
* | |
* 1/4 3/8 * 1/8 { this voice is given. *: startBeat is not used
* | |
* startBeat_| |_endBeat
*
* Because startBeat is not used, we compute the distance
* from the last used beat to the end beat, which is known
* from the given spacing elements:
*
* 1/4 3/8 * 1/8
* |_____________|
* distanceToEndBeat
*
* And we subtract distance between the already computed offset of
* the last used beat and the also already computed offset of
* the starting beat (both given in the list of beat offsets):
*
* 1/4 3/8 * 1/8
* |_________|
* distanceToLastUsedBeat
*
* The result is the distance between the starting beat
* and the ending beat:
*
* 1/4 3/8 * 1/8
* |___|
* return
*
* This value is the minimal distance the given voice needs to
* place the elements up to the given ending beat.
*/
float computeMinimalDistance(Fraction startBeat, Fraction endBeat, boolean endBeatIsMeasureEnd, Voice voice, List<ElementSpacing> spacings, List<BeatOffset> alreadyComputedBeatOffsets, float interlineSpace) {
// end beat used? (measure end beat is always used)
if (endBeatIsMeasureEnd || voice.isBeatUsed(endBeat)) {
// when measure is incomplete: use last available beat
if (endBeatIsMeasureEnd) {
endBeat = voice.getLastUsedBeat(endBeat);
}
float endOffset = getLastOffset(spacings, endBeat) * interlineSpace;
// start beat used?
if (voice.isBeatUsed(startBeat)) {
// yes
float startOffset = getLastOffset(spacings, startBeat) * interlineSpace;
// return the distance between this two beats
return endOffset - startOffset;
} else {
// no, start beat is not used. use the algorithm described above
Fraction lastUsedBeat = voice.getLastUsedBeat(startBeat);
// get offset of the last used beat in the voice spacing
float lastUsedBeatVoiceSpacingOffset = 0;
for (ElementSpacing spacing : spacings) {
if (spacing.beat.equals(lastUsedBeat)) {
lastUsedBeatVoiceSpacingOffset = spacing.xIs * interlineSpace;
break;
}
}
// compute minimal distance from last used beat to end beat
float distanceToEndBeat = endOffset - lastUsedBeatVoiceSpacingOffset;
// get offset of the last computed beat from the list of already computed beat offsets
float lastComputedBeatOffset = alreadyComputedBeatOffsets.get(alreadyComputedBeatOffsets.size() - 1).getOffsetMm();
// get offset of the last used beat from the list of already computed beat offsets
float lastUsedBeatBeatOffsetsOffset = 0;
for (BeatOffset beatOffset : alreadyComputedBeatOffsets) {
if (beatOffset.getBeat().equals(lastUsedBeat)) {
lastUsedBeatBeatOffsetsOffset = beatOffset.getOffsetMm();
break;
}
}
// compute distance between these two offsets
float distanceToLastUsedBeat = lastComputedBeatOffset - lastUsedBeatBeatOffsetsOffset;
// return the distance between the last computed beat offset and the end beat
return distanceToEndBeat - distanceToLastUsedBeat;
}
} else {
// since there is no element, we need no space
return 0;
}
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class VoicesBeatOffsetter method computeVoicesBeats.
/**
* Returns a sorted list of all beats, where
* chords or rests begin, from the given list of voice spacings.
* There are no duplicate beats. The ending beats of the voices are not added.
*/
SortedList<Fraction> computeVoicesBeats(List<VoiceSpacing> voiceSpacings) {
SortedList<Fraction> beats = sortedListNoDuplicates();
Fraction beat;
for (VoiceSpacing voiceSpacing : voiceSpacings) {
beat = Fraction._0;
for (ElementSpacing spacingElement : voiceSpacing.elements) {
MusicElement element = spacingElement.getElement();
if (element instanceof VoiceElement) {
// add beat
beats.add(beat);
// find the next beat
beat = beat.add(((VoiceElement) element).getDuration());
}
}
// do not add beat here, because the ending beat of an incomplete measure
// is not interesting for computing beat offsets.
}
return beats;
}
Aggregations