Search in sources :

Example 16 with ElementSpacing

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;
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) BeatOffset(com.xenoage.zong.musiclayout.spacing.BeatOffset) Fraction(com.xenoage.utils.math.Fraction) VoiceSpacing(com.xenoage.zong.musiclayout.spacing.VoiceSpacing)

Example 17 with ElementSpacing

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();
}
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 18 with ElementSpacing

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);
}
Also used : ElementWidth(com.xenoage.zong.musiclayout.spacing.ElementWidth) LeadingSpacing(com.xenoage.zong.musiclayout.spacing.LeadingSpacing) SimpleSpacing(com.xenoage.zong.musiclayout.spacing.SimpleSpacing) Clef(com.xenoage.zong.core.music.clef.Clef) ClefNotation(com.xenoage.zong.musiclayout.notation.ClefNotation) TraditionalKey(com.xenoage.zong.core.music.key.TraditionalKey) MusicContext(com.xenoage.zong.core.music.MusicContext) ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) TraditionalKeyNotation(com.xenoage.zong.musiclayout.notation.TraditionalKeyNotation) TraditionalKey(com.xenoage.zong.core.music.key.TraditionalKey) Key(com.xenoage.zong.core.music.key.Key)

Example 19 with ElementSpacing

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

Example 20 with ElementSpacing

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;
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) Stamping(com.xenoage.zong.musiclayout.stampings.Stamping) MusicElement(com.xenoage.zong.core.music.MusicElement) LeadingSpacing(com.xenoage.zong.musiclayout.spacing.LeadingSpacing) Notation(com.xenoage.zong.musiclayout.notation.Notation) ClefNotation(com.xenoage.zong.musiclayout.notation.ClefNotation) TimeNotation(com.xenoage.zong.musiclayout.notation.TimeNotation) TraditionalKeyNotation(com.xenoage.zong.musiclayout.notation.TraditionalKeyNotation)

Aggregations

ElementSpacing (com.xenoage.zong.musiclayout.spacing.ElementSpacing)26 VoiceSpacing (com.xenoage.zong.musiclayout.spacing.VoiceSpacing)17 Test (org.junit.Test)11 BeatOffset (com.xenoage.zong.musiclayout.spacing.BeatOffset)10 ElementWidth (com.xenoage.zong.musiclayout.spacing.ElementWidth)7 Fraction (com.xenoage.utils.math.Fraction)6 VoiceElement (com.xenoage.zong.core.music.VoiceElement)6 Voice (com.xenoage.zong.core.music.Voice)5 Clef (com.xenoage.zong.core.music.clef.Clef)5 ChordNotation (com.xenoage.zong.musiclayout.notation.ChordNotation)5 Notation (com.xenoage.zong.musiclayout.notation.Notation)5 RestNotation (com.xenoage.zong.musiclayout.notation.RestNotation)5 MusicElement (com.xenoage.zong.core.music.MusicElement)4 VoiceTest (com.xenoage.zong.core.music.VoiceTest)4 ClefNotation (com.xenoage.zong.musiclayout.notation.ClefNotation)4 Notations (com.xenoage.zong.musiclayout.notation.Notations)4 LayoutSettingsTest (com.xenoage.zong.musiclayout.settings.LayoutSettingsTest)4 LeadingSpacing (com.xenoage.zong.musiclayout.spacing.LeadingSpacing)4 TraditionalKeyNotation (com.xenoage.zong.musiclayout.notation.TraditionalKeyNotation)3 Stamping (com.xenoage.zong.musiclayout.stampings.Stamping)3