package com.xenoage.zong.musiclayout.spacer.beam; import com.xenoage.zong.musiclayout.notation.BeamNotation; import com.xenoage.zong.musiclayout.notation.chord.StemNotation; import com.xenoage.zong.musiclayout.spacer.beam.stem.BeamedStems; import com.xenoage.zong.musiclayout.spacing.BeamSpacing; import com.xenoage.zong.musiclayout.spacing.SystemSpacing; import lombok.val; import static com.xenoage.utils.kernel.Range.range; import static com.xenoage.utils.math.MathUtils.interpolateLinear; import static com.xenoage.zong.core.music.util.DurationInfo.getFlagsCount; import static com.xenoage.zong.musiclayout.SLP.slp; import static com.xenoage.zong.musiclayout.spacer.beam.BeamSpacer.beamSpacer; import static com.xenoage.zong.musiclayout.spacer.beam.placement.TwoStavesBeamPlacer.twoStavesBeamPlacer; import static com.xenoage.zong.musiclayout.spacer.beam.slant.TwoStavesBeamSlanter.twoStavesBeamSlanter; /** * Creates the {@link BeamSpacing} for a beam on two adjacent staves. * * First, the preferred slant is computed. Then, the length of the first and last stem * is computed. Finally, the lengths of the inner stems are interpolated. * * @author Andreas Wenger */ public class TwoStavesBeamSpacer { static final TwoStavesBeamSpacer twoStavesBeamSpacer = new TwoStavesBeamSpacer(); BeamSpacing compute(BeamNotation beam, SystemSpacing system) { //compute slant val slant = twoStavesBeamSlanter.compute(beam); //compute the ends of the first and last stem val chords = beamSpacer.getBeamChordSpacings(beam, system); val stems = BeamedStems.fromBeam(chords); val placement = twoStavesBeamPlacer.compute(slant, stems, beam, system); //adjust the stem lengths by interpolating //the end LPs of the stems have then to be relative to the beam's staff (the staff of the first chord) int beamStaffIndex = beam.mp.staff; float leftEndYMm = system.getYMm(placement.leftSlp); float rightEndYMm = system.getYMm(placement.rightSlp); float beamRightEndLp = 0; //may be on different staff, so this is not always equal to placement.rightSlp.lp for (int iChord : range(stems)) { float yMm = interpolateLinear(leftEndYMm, rightEndYMm, stems.leftXIs, stems.rightXIs, stems.get(iChord).xIs); float lp = system.getLp(beamStaffIndex, yMm); if (iChord == stems.getCount() - 1) beamRightEndLp = lp; //when the note is not on the primary staff, but on the other staff, we have to lengthen the stem //so that it touches each beam line if (stems.get(iChord).dir != stems.primaryStemDir) { int linesCountAtChord = getFlagsCount(beam.chords.get(iChord).element.getDuration()); lp += stems.get(iChord).dir.getSign() * 2 * ((linesCountAtChord - 1) * beam.lineHeightIs + linesCountAtChord * beam.gapIs); } StemNotation stem = beam.chords.get(iChord).stem; if (stem != null) //it could be possible that there is no stem stem.endSlp = slp(beamStaffIndex, lp); //before, maybe the end LP was relative to another staff } return new BeamSpacing(beam, placement.leftSlp.lp, beamRightEndLp, chords); } }