use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class VoicesBeatOffsetter method compute.
/**
* Computes the offsets of all the used beats, including
* at least beat 0 and the beat at the end of the measure.
* The beats containing the notes with the lowest valuation
* (or that needs accidentals) dictate the spacing.
* See "Ross: The Art of Music Engraving", page 79.
*/
public List<BeatOffset> compute(List<VoiceSpacing> voiceSpacings, Fraction measureBeats, float minimalBeatsOffsetIs) {
// the list of all used beats of the measure
// TODO: handle upbeat measures correctly!
SortedList<Fraction> beats = computeVoicesBeats(voiceSpacings);
// add final used beat
beats.add(computeLastBeat(voiceSpacings));
// add final beat in terms of time signature (only correct for non-upbeat measures)
beats.add(measureBeats);
// the resulting offsets for each used beat
ArrayList<BeatOffset> ret = alist();
// compute the offset of beat 0
float offsetMm = getOffsetBeat0InMm(voiceSpacings);
Fraction lastBeat = _0;
ret.add(new BeatOffset(lastBeat, offsetMm));
// Otherwise we must find the dominant parts within the voices
if (voiceSpacings.size() == 1) {
// only one voice
float interlineSpace = voiceSpacings.get(0).interlineSpace;
for (ElementSpacing se : voiceSpacings.get(0).elements) {
// if last beat offset has same beat, overwrite it
if (ret.get(ret.size() - 1).getBeat().equals(se.beat))
ret.remove(ret.size() - 1);
ret.add(new BeatOffset(se.beat, se.xIs * interlineSpace));
}
} else {
// more than one voice
// use the following algorithm:
// for each beat, compute the offset, by asking each voice how much space
// it requires between the last computed beat offset and the current one.
// each time, take the greatest distance required.
Iterator<Fraction> beatsIterator = beats.iterator();
// ignore beat 0, we have handled it before
beatsIterator.next();
while (beatsIterator.hasNext()) {
Fraction beat = beatsIterator.next();
// find dominating voice and its minimal required distance
float minimalDistance = 0;
for (VoiceSpacing voiceSpacing : voiceSpacings) {
float interlineSpace = voiceSpacing.interlineSpace;
float voiceMinimalDistance = computeMinimalDistance(lastBeat, beat, beat.equals(measureBeats), voiceSpacing.voice, voiceSpacing.elements, ret, interlineSpace);
minimalDistance = Math.max(minimalDistance, voiceMinimalDistance);
// but we do not want to have different beats at the same offset, so add a small distance.
if (minimalDistance < minimalBeatsOffsetIs * interlineSpace) {
minimalDistance = minimalBeatsOffsetIs * interlineSpace;
}
}
// add beat
offsetMm += minimalDistance;
ret.add(new BeatOffset(beat, offsetMm));
lastBeat = beat;
}
}
ret.trimToSize();
return ret;
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class StretchMeasures method compute.
@Override
public void compute(SystemSpacing system, float usableWidthMm) {
// compute width of all voice spacings
// (leading spacings are not stretched)
float voicesWidthMm = 0;
float leadingsWidthMm = 0;
for (ColumnSpacing column : system.columns) {
voicesWidthMm += column.getVoicesWidthMm();
leadingsWidthMm += column.getLeadingWidthMm();
}
// compute the stretching factor for the voice spacings
if (voicesWidthMm == 0)
return;
float stretch = (usableWidthMm - leadingsWidthMm) / voicesWidthMm;
// measure columns
for (ColumnSpacing column : system.columns) {
// beat offsets
for (int i : range(column.beatOffsets)) {
BeatOffset bo = column.beatOffsets.get(i);
BeatOffset stretched = bo.withOffsetMm(bo.offsetMm * stretch);
column.beatOffsets.set(i, stretched);
}
for (int i : range(column.barlineOffsets)) {
BeatOffset bo = column.barlineOffsets.get(i);
BeatOffset stretched = bo.withOffsetMm(bo.offsetMm * stretch);
column.barlineOffsets.set(i, stretched);
}
// measures
for (MeasureSpacing measure : column.measures) {
// measure elements
for (ElementSpacing element : measure.elements) {
// stretch the offset
element.xIs *= stretch;
}
// voices
for (VoiceSpacing voice : measure.voices) {
// traverse elements in reverse order, so we can align grace elements correctly
// grace elements are not stretched, but the distance to their following full element
// stays the same
float lastElementOriginalOffsetIs = getLast(column.beatOffsets).offsetMm / voice.interlineSpace;
for (int i : rangeReverse(voice.elements)) {
ElementSpacing element = voice.elements.get(i);
if (element.isGrace()) {
// grace element: keep distance to following element
float oldDistance = lastElementOriginalOffsetIs - element.xIs;
lastElementOriginalOffsetIs = element.xIs;
element.xIs = voice.elements.get(i + 1).xIs - oldDistance;
} else {
// normal element: stretch the offset
lastElementOriginalOffsetIs = element.xIs;
element.xIs *= stretch;
}
}
}
}
}
// full system width
system.widthMm = usableWidthMm;
// columns have been changed
system.onColumnsWidthChange();
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class LeadingSpacer method compute.
/**
* Computes the {@link LeadingSpacing} for the current measure.
*/
public LeadingSpacing compute(Context context, Notations notations) {
float xOffset = context.settings.offsetMeasureStart;
boolean useKey = false;
MusicContext musicContext = context.getMusicContext(At, null);
Key key = musicContext.getKey();
if (key instanceof TraditionalKey) {
useKey = true;
}
List<ElementSpacing> elements = alist(useKey ? 2 : 1);
// it is not the same element instance, but has the same meaning
Clef clef = new Clef(musicContext.getClef());
ClefNotation clefNotation = new ClefNotation(clef, new ElementWidth(0, context.settings.spacings.widthClef, 0), musicContext.getClef().getLp(), 1);
notations.add(clefNotation);
xOffset += context.settings.spacings.widthClef / 2;
elements.add(new SimpleSpacing(clefNotation, fr(0), xOffset));
xOffset += context.settings.spacings.widthClef / 2;
if (useKey) {
TraditionalKey tkey = (TraditionalKey) key;
xOffset += context.settings.spacings.widthDistanceMin;
// it is not the same element instance, but has the same meaning
TraditionalKey tradKey = new TraditionalKey(tkey.getFifths(), tkey.getMode());
TraditionalKeyNotation keyNotation = traditionalKeyNotator.compute(tradKey, context);
notations.add(keyNotation);
elements.add(new SimpleSpacing(keyNotation, fr(0), xOffset));
xOffset += keyNotation.getWidth().getWidth();
}
return new LeadingSpacing(elements, xOffset);
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class MeasureElementsSpacer method getNearestSpacingElements.
/**
* Gets the nearest two {@link ElementSpacing}s at the
* given beat (left [0] and right [1]).
*/
private ElementSpacing[] getNearestSpacingElements(Fraction beat, List<VoiceSpacing> vss) {
ElementSpacing[] ret = { null, null };
float retLeftX = Float.MIN_VALUE;
float retRightX = Float.MAX_VALUE;
for (VoiceSpacing vs : vss) {
for (ElementSpacing se : vs.elements) {
int compare = se.beat.compareTo(beat);
if (compare < 0) {
float leftX = getLeftX(se);
if (leftX > retLeftX) {
// found nearer left element
retLeftX = leftX;
ret[0] = se;
}
} else if (compare >= 0) {
float rightX = getRightX(se);
if (rightX < retRightX) {
// found nearer right element
retRightX = rightX;
ret[1] = se;
}
// first candidate always matches here
break;
}
}
}
return ret;
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class MeasureStamper method stampLeading.
/**
* Stamps the {@link MeasureElement}s of the leading spacing.
* @param leadingXMm the horizontal position on the staff in mm, where
* the leading spacing of the measure starts.
*/
public List<Stamping> stampLeading(MeasureSpacing measure, float leadingXMm, StamperContext context) {
LeadingSpacing leading = measure.leading;
if (leading == null)
return emptyList;
List<Stamping> ret = alist(leading.elements.size());
for (ElementSpacing element : leading.elements) {
MusicElement me = element.getElement();
if (me != null) {
float xMm = leadingXMm + element.xIs * measure.interlineSpace;
Notation notation = context.getNotation(me);
if (notation == null)
throw new RuntimeException("No notation for element " + me + " at " + MP.getMP(me));
ret.add(stamp(notation, xMm, context));
}
}
return ret;
}
Aggregations