Search in sources :

Example 11 with VoiceSpacing

use of com.xenoage.zong.musiclayout.spacing.VoiceSpacing 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.getMaxIS());
    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.Companion.get_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);
}
Also used : LeadingSpacing(com.xenoage.zong.musiclayout.spacing.LeadingSpacing) ColumnSpacing(com.xenoage.zong.musiclayout.spacing.ColumnSpacing) Fraction(com.xenoage.utils.math.Fraction) ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) ColumnHeader(com.xenoage.zong.core.header.ColumnHeader) Column(com.xenoage.zong.core.music.util.Column) BarlinesBeatOffsetter(com.xenoage.zong.musiclayout.spacer.beat.BarlinesBeatOffsetter) MP.atMeasure(com.xenoage.zong.core.position.MP.atMeasure) Measure(com.xenoage.zong.core.music.Measure) MeasureSpacing(com.xenoage.zong.musiclayout.spacing.MeasureSpacing) VoiceSpacingsByStaff(com.xenoage.zong.musiclayout.layouter.columnspacing.VoiceSpacingsByStaff) BeatOffset(com.xenoage.zong.musiclayout.spacing.BeatOffset) ArrayList(java.util.ArrayList) List(java.util.List) VoiceSpacing(com.xenoage.zong.musiclayout.spacing.VoiceSpacing) Voice(com.xenoage.zong.core.music.Voice)

Example 12 with VoiceSpacing

use of com.xenoage.zong.musiclayout.spacing.VoiceSpacing 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;
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) VoiceSpacing(com.xenoage.zong.musiclayout.spacing.VoiceSpacing)

Example 13 with VoiceSpacing

use of com.xenoage.zong.musiclayout.spacing.VoiceSpacing 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();
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) ColumnSpacing(com.xenoage.zong.musiclayout.spacing.ColumnSpacing) MeasureSpacing(com.xenoage.zong.musiclayout.spacing.MeasureSpacing) BeatOffset(com.xenoage.zong.musiclayout.spacing.BeatOffset) VoiceSpacing(com.xenoage.zong.musiclayout.spacing.VoiceSpacing)

Example 14 with VoiceSpacing

use of com.xenoage.zong.musiclayout.spacing.VoiceSpacing 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;
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) VoiceSpacing(com.xenoage.zong.musiclayout.spacing.VoiceSpacing)

Example 15 with VoiceSpacing

use of com.xenoage.zong.musiclayout.spacing.VoiceSpacing in project Zong by Xenoage.

the class SingleVoiceSpacer method compute.

VoiceSpacing compute(Voice voice, float interlineSpace, Fraction measureBeats, int staffLinesCount, Notations notations, LayoutSettings layoutSettings) {
    LinkedList<ElementSpacing> ret = llist();
    // special case: no elements in the measure.
    if (voice.getElements().size() == 0) {
        return new VoiceSpacing(voice, interlineSpace, alist((ElementSpacing) new BorderSpacing(Fraction.Companion.get_0(), 0), new BorderSpacing(measureBeats, layoutSettings.spacings.widthMeasureEmpty)));
    }
    // we compute the spacings in reverse order. this is easier, since grace chords
    // use shared space when possible, but are aligned to the right. so we begin
    // at position 0, and create the spacings in reverse direction (thus we find negative positions).
    // at the end, we shift the voice spacing to the right to be aligned at the left measure border,
    // which is position 0.
    // last symbol offset:
    // real offset where the last element's symbol started
    // since we do not know the right border yet, we start at 0
    float lastSymbolOffset = 0;
    // front gap offset:
    // offset where the last element (including front gap) started.
    float lastFrontGapOffset = lastSymbolOffset;
    // last full element offset:
    // lastSymbolOffset of the last full (non-grace) element
    float lastFullSymbolOffset = lastSymbolOffset;
    // at last beat
    Fraction curBeat = voice.getFilledBeats();
    ret.addFirst(new BorderSpacing(curBeat, lastFrontGapOffset));
    // iterate through the elements in reverse order
    for (VoiceElement element : Companion.reverseIt(voice.getElements())) {
        // get the notation
        Notation notation = notations.get(element);
        if (notation == null)
            throw new IllegalStateException("No notation for element " + element);
        // get the width of the element (front gap, symbol's width, rear gap, lyric's width)
        ElementWidth elementWidth = notation.getWidth();
        // add spacing for voice element
        float symbolOffset;
        boolean grace = !element.getDuration().isGreater0();
        if (!grace) {
            // full element
            // share this rear gap and the front gap of the following
            // element + the space of following grace elements, when possible
            // (but use at least minimal distance)
            symbolOffset = Math.min(lastFrontGapOffset - layoutSettings.spacings.widthDistanceMin, lastFullSymbolOffset - elementWidth.rearGap) - elementWidth.symbolWidth;
            lastFullSymbolOffset = symbolOffset;
            // update beat cursor
            curBeat = curBeat.sub(element.getDuration());
        } else {
            // grace element
            // share this rear gap and the front gap of the following element, when possible
            symbolOffset = Math.min(lastFrontGapOffset, lastSymbolOffset - elementWidth.rearGap) - elementWidth.symbolWidth;
        }
        ElementSpacing elementSpacing = null;
        if (notation instanceof RestNotation) {
            // rest spacing
            elementSpacing = restSpacer.compute((RestNotation) notation, curBeat, symbolOffset, staffLinesCount);
        } else {
            // chord spacing
            elementSpacing = new ChordSpacing((ChordNotation) notation, curBeat, symbolOffset);
        }
        ret.addFirst(elementSpacing);
        lastFrontGapOffset = symbolOffset - elementWidth.frontGap;
        lastSymbolOffset = symbolOffset;
    }
    // shift spacings to the right
    float shift = (-lastFrontGapOffset) + layoutSettings.offsetMeasureStart;
    for (ElementSpacing e : ret) e.xIs += shift;
    return new VoiceSpacing(voice, interlineSpace, ilist(ret));
}
Also used : ElementWidth(com.xenoage.zong.musiclayout.spacing.ElementWidth) ChordNotation(com.xenoage.zong.musiclayout.notation.ChordNotation) RestNotation(com.xenoage.zong.musiclayout.notation.RestNotation) Fraction(com.xenoage.utils.math.Fraction) Notation(com.xenoage.zong.musiclayout.notation.Notation) RestNotation(com.xenoage.zong.musiclayout.notation.RestNotation) ChordNotation(com.xenoage.zong.musiclayout.notation.ChordNotation) ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) ChordSpacing(com.xenoage.zong.musiclayout.spacing.ChordSpacing) BorderSpacing(com.xenoage.zong.musiclayout.spacing.BorderSpacing) VoiceElement(com.xenoage.zong.core.music.VoiceElement) VoiceSpacing(com.xenoage.zong.musiclayout.spacing.VoiceSpacing)

Aggregations

VoiceSpacing (com.xenoage.zong.musiclayout.spacing.VoiceSpacing)22 ElementSpacing (com.xenoage.zong.musiclayout.spacing.ElementSpacing)17 Test (org.junit.Test)12 BeatOffset (com.xenoage.zong.musiclayout.spacing.BeatOffset)9 Voice (com.xenoage.zong.core.music.Voice)7 VoiceElement (com.xenoage.zong.core.music.VoiceElement)6 ChordNotation (com.xenoage.zong.musiclayout.notation.ChordNotation)6 ColumnSpacing (com.xenoage.zong.musiclayout.spacing.ColumnSpacing)6 RestNotation (com.xenoage.zong.musiclayout.notation.RestNotation)5 ElementWidth (com.xenoage.zong.musiclayout.spacing.ElementWidth)5 Fraction (com.xenoage.utils.math.Fraction)4 VoiceTest (com.xenoage.zong.core.music.VoiceTest)4 Notations (com.xenoage.zong.musiclayout.notation.Notations)4 LayoutSettingsTest (com.xenoage.zong.musiclayout.settings.LayoutSettingsTest)4 MeasureSpacing (com.xenoage.zong.musiclayout.spacing.MeasureSpacing)4 SystemSpacing (com.xenoage.zong.musiclayout.spacing.SystemSpacing)4 ChordSpacing (com.xenoage.zong.musiclayout.spacing.ChordSpacing)3 Chord (com.xenoage.zong.core.music.chord.Chord)2 ChordFactory.graceChord (com.xenoage.zong.core.music.chord.ChordFactory.graceChord)2 Clef (com.xenoage.zong.core.music.clef.Clef)2