use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class SingleVoiceSpacerTest method testSimple.
/**
* Computes a simple voice spacing.
* <pre>
* Single elements: [-r1---][-r2-][-r3--][----r4--]
* Combined: --r1--~~r2_~-r3_~~---r4---
* </pre> (~: area used by two elements, _: minimal distance between elements)
*/
@Test
public void testSimple() {
// create voice and notations
Voice voice = new Voice(alist((VoiceElement) r1, r2, r3, r4));
Notations notations = new Notations();
notations.add(new RestNotation(r1, new ElementWidth(2, 2, 4), null));
notations.add(new RestNotation(r2, new ElementWidth(2, 2, 2), null));
notations.add(new RestNotation(r3, new ElementWidth(2, 2, 3), null));
notations.add(new RestNotation(r4, new ElementWidth(5, 2, 3), null));
// compute spacing
VoiceSpacing vs = testee.compute(voice, 200f, fr(4, 4), 5, notations, layoutSettings);
// check spacing
ElementSpacing[] ses = vs.elements.toArray(new ElementSpacing[0]);
float s = layoutSettings.offsetMeasureStart;
float d = layoutSettings.spacings.widthDistanceMin;
assertEquals(5, ses.length);
assertEquals(s + 2, ses[0].xIs, DELTA_FLOAT);
assertEquals(s + 8, ses[1].xIs, DELTA_FLOAT);
assertEquals(s + 12 + d, ses[2].xIs, DELTA_FLOAT);
assertEquals(s + 19 + 2 * d, ses[3].xIs, DELTA_FLOAT);
assertEquals(s + 24 + 2 * d, ses[4].xIs, DELTA_FLOAT);
// check beats
assertEquals(fr(0, 8), ses[0].beat);
assertEquals(fr(2, 8), ses[1].beat);
assertEquals(fr(3, 8), ses[2].beat);
assertEquals(fr(4, 8), ses[3].beat);
assertEquals(fr(8, 8), ses[4].beat);
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class SingleVoiceSpacerTest method testGrace2.
/**
* Computes a voice spacing with grace notes,
* where the element before the grace notes has enough empty rear space
* to take at least one of the grace notes.
* <pre>
* Single elements: [-r1------][g1][g2][--r4--]
* Combined: --r1_~g1~g2~~~r4---
* </pre> (~: area used by two elements, _: minimal distance between elements)
*/
@Test
public void testGrace2() {
// create voice and notations
Voice voice = new Voice(alist((VoiceElement) r1, g1, g2, r4));
Notations notations = new Notations();
notations.add(new RestNotation(r1, new ElementWidth(2, 2, 7), null));
notations.add(new ChordNotation(g1, new ElementWidth(1, 2, 1)));
notations.add(new ChordNotation(g2, new ElementWidth(1, 2, 1)));
notations.add(new RestNotation(r4, new ElementWidth(3, 2, 3), null));
// compute spacing
VoiceSpacing vs = testee.compute(voice, 400f, fr(4, 4), 5, notations, layoutSettings);
// check spacing
ElementSpacing[] ses = vs.elements.toArray(new ElementSpacing[0]);
;
float s = layoutSettings.offsetMeasureStart;
float d = layoutSettings.spacings.widthDistanceMin;
assertEquals(5, ses.length);
assertEquals(s + 2, ses[0].xIs, DELTA_FLOAT);
assertEquals(s + 5 + d, ses[1].xIs, DELTA_FLOAT);
assertEquals(s + 8 + d, ses[2].xIs, DELTA_FLOAT);
assertEquals(s + 13 + d, ses[3].xIs, DELTA_FLOAT);
assertEquals(s + 18 + d, ses[4].xIs, DELTA_FLOAT);
// check beats
assertEquals(fr(0, 8), ses[0].beat);
assertEquals(fr(2, 8), ses[1].beat);
assertEquals(fr(2, 8), ses[2].beat);
assertEquals(fr(2, 8), ses[3].beat);
assertEquals(fr(6, 8), ses[4].beat);
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class ColumnSpacer method compute.
/**
* Computes a {@link ColumnSpacing} from a measure column.
* @param context the current context, with the current {@link MP} and precomputed
* element {@link Notation}s
* @param createLeading true, if a leading spacing has to be created, otherwise false
* @param notations the precomputed notations of the measure and voice elements
*/
public ColumnSpacing compute(Context context, boolean createLeading, Notations notations) {
context.saveMp();
int measureIndex = context.mp.measure;
Column column = context.score.getColumn(measureIndex);
ColumnHeader columnHeader = context.score.getHeader().getColumnHeader(measureIndex);
// compute the optimal spacings for each voice separately
List<List<VoiceSpacing>> voiceSpacingsByStaff = alist();
for (int iStaff : range(column)) {
List<VoiceSpacing> vss = alist();
Measure measure = column.get(iStaff);
for (Voice voice : measure.getVoices()) {
context.mp = MP.atVoice(iStaff, measureIndex, measure.getVoices().indexOf(voice));
VoiceSpacing vs = singleVoiceSpacer.compute(context, notations);
vss.add(vs);
}
voiceSpacingsByStaff.add(vss);
}
// compute the measure elements (like inner clefs) and accordingly updated voice spacings
ArrayList<List<ElementSpacing>> optimalMeasureElementsSpacingsByStaff = alist();
for (int iStaff : range(column)) {
context.mp = MP.atMeasure(iStaff, measureIndex);
List<ElementSpacing> measureSpacing = measureElementsSpacer.compute(context, createLeading, voiceSpacingsByStaff.get(iStaff), notations);
optimalMeasureElementsSpacingsByStaff.add(measureSpacing);
}
// compute the common beat offsets of this measure column
Fraction measureBeats = context.score.getMeasureBeats(measureIndex);
VoiceSpacingsByStaff voiceSpacings = new VoiceSpacingsByStaff(voiceSpacingsByStaff);
List<BeatOffset> beatOffsets = voicesBeatOffsetter.compute(voiceSpacings.getAll(), measureBeats, context.settings.offsetBeatsMinimal);
// recompute beat offsets with respect to barlines
BarlinesBeatOffsetter.Result offsets = barlinesBeatOffsetter.compute(beatOffsets, columnHeader, context.score.getMaxInterlineSpace());
beatOffsets = offsets.voiceElementOffsets;
List<BeatOffset> barlineOffsets = offsets.barlineOffsets;
// compute the spacings for the whole column, so that equal beats are aligned
ArrayList<List<ElementSpacing>> alignedMeasureElementsSpacingsByStaff = alist();
for (int iStaff : range(column)) {
Measure measure = column.get(iStaff);
// voice spacings
for (int iVoice : range(measure.getVoices())) alignedVoicesSpacer.compute(voiceSpacings.get(iStaff, iVoice), beatOffsets);
// measure elements, based on the aligned voice spacings
context.mp = atMeasure(iStaff, measureIndex);
alignedMeasureElementsSpacingsByStaff.add(measureElementsSpacer.compute(context, createLeading, voiceSpacings.getStaff(iStaff), notations));
}
// compute spacings for each staff
List<MeasureSpacing> measureSpacings = alist(column.size());
for (int iStaff : range(column)) {
// create leading spacing, if needed
LeadingSpacing leadingSpacing = null;
if (createLeading) {
context.mp = atBeat(iStaff, measureIndex, 0, Fraction._0);
leadingSpacing = leadingSpacer.compute(context, notations);
}
// create measure spacing
float interlineSpace = context.score.getInterlineSpace(iStaff);
measureSpacings.add(new MeasureSpacing(atMeasure(iStaff, measureIndex), interlineSpace, voiceSpacings.getStaff(iStaff), alignedMeasureElementsSpacingsByStaff.get(iStaff), leadingSpacing));
}
context.restoreMp();
return new ColumnSpacing(measureIndex, measureSpacings, beatOffsets, barlineOffsets);
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class MeasureElementsSpacer method getFirstElementSpacing.
/**
* Gets the leftmost {@link ElementSpacing} in the given list of {@link VoiceSpacing}s,
* or null there is none.
*/
private ElementSpacing getFirstElementSpacing(List<VoiceSpacing> vss) {
ElementSpacing ret = null;
float retLeftX = Float.MAX_VALUE;
for (VoiceSpacing vs : vss) {
for (ElementSpacing se : vs.elements) {
float leftX = getLeftX(se);
if (leftX < retLeftX) {
retLeftX = leftX;
ret = se;
// no other element after this one in the same voice will be more left
break;
}
}
}
return ret;
}
use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.
the class MeasureElementsSpacer method compute.
List<ElementSpacing> compute(BeatEList<Clef> clefs, @MaybeEmpty BeatEList<Key> keys, @MaybeNull TimeSignature time, boolean existsLeadingSpacing, List<VoiceSpacing> voiceSpacings, int staff, Notations notations, LayoutSettings layoutSettings) {
Key key0 = null;
if (keys.size() > 0 && keys.getFirst().beat.equals(_0))
key0 = keys.getFirst().element;
if (key0 == null && time == null && (clefs == null || clefs.size() == 0)) {
// nothing to do
return empty;
}
ArrayList<ElementSpacing> ret = alist();
float startOffset = layoutSettings.offsetMeasureStart;
// key and time
// ************
boolean isKey = !existsLeadingSpacing && key0 instanceof TraditionalKey;
boolean isTime = time != null;
if (isKey || isTime) {
float currentOffset = startOffset;
// ***
if (isKey) {
Notation keyNotation = notations.get(key0, staff);
ret.add(new SimpleSpacing(keyNotation, _0, startOffset));
currentOffset += keyNotation.getWidth().getUsedWidth();
}
// ****
if (time != null) {
Notation timeNotation = notations.get(time, staff);
ret.add(new SimpleSpacing(timeNotation, _0, currentOffset + timeNotation.getWidth().symbolWidth / 2));
currentOffset += timeNotation.getWidth().getUsedWidth();
}
// move voice elements, if not enough space before first voice element
ElementSpacing leftSE = getFirstElementSpacing(voiceSpacings);
if (leftSE != null) {
float leftSEx = getLeftX(leftSE);
// existing space
float ES = leftSEx;
// additional needed space
float AS = currentOffset - ES;
if (AS > 0) {
shift(voiceSpacings, AS);
startOffset += AS;
}
}
}
// voice 2: 1 o
if (clefs != null) {
for (BeatE<Clef> ME : clefs) {
Fraction MEb = ME.beat;
Notation MEnotation = notations.get(ME.element);
float MEwidth = MEnotation.getWidth().getWidth();
// if there is a leading spacing, ignore elements at beat 0
if (existsLeadingSpacing && !MEb.isGreater0())
continue;
// find VE1 and VE2 for the current element
ElementSpacing[] ses = getNearestSpacingElements(MEb, voiceSpacings);
ElementSpacing VE1 = ses[0], VE2 = ses[1];
// if VE1 is unknown, use startOffset. if VE2 is unknown, ignore this element
float VE1x = (VE1 != null ? getRightX(VE1) : startOffset);
if (VE2 == null)
continue;
float VE2x = getLeftX(VE2);
// existing space
float ES = VE2x - VE1x - 2 * layoutSettings.spacings.widthDistanceMin;
if (ES < MEwidth) {
// additional space needed
float AS = MEwidth - ES;
// move all elements at or after ME.beat
VE2x += AS;
shiftAfterBeat(voiceSpacings, AS, MEb);
}
// add measure element
float MEx = VE2x - layoutSettings.spacings.widthDistanceMin - MEwidth / 2;
ret.add(new SimpleSpacing(MEnotation, ME.beat, MEx));
}
}
ret.trimToSize();
return ret;
}
Aggregations