use of com.xenoage.zong.core.music.chord.StemDirection in project Zong by Xenoage.
the class ChordNotator method compute.
public ChordNotation compute(Chord chord, Context context, @MaybeNull Notations notations) {
Score score = context.score;
float interlineSpace = score.getInterlineSpace(context.mp);
FontInfo lyricsFont = score.getFormat().getLyricFont();
MusicContext mc = score.getMusicContext(context.mp, BeforeOrAt, Before);
// grace or normal chord?
boolean grace = chord.isGrace();
ChordWidths chordWidths = (grace ? context.settings.graceChordWidths : context.settings.chordWidths);
ChordSpacings spacings = (grace ? context.settings.spacings.graceChordSpacings : context.settings.spacings.normalChordSpacings);
// use or compute stem direction
StemDirection stemDirection = chord.getStem().getDirection();
if (stemDirection == StemDirection.Default) {
// if stem direction was not computed yet, compute it now
if (notations != null)
stemDirection = notations.getChord(chord).stemDirection;
if (stemDirection == StemDirection.Default) {
Map<Chord, StemDirection> computedStems = stemDirector.compute(chord);
stemDirection = computedStems.get(chord);
// also remember the other computed stems
if (notations != null)
for (Chord computedChord : computedStems.keySet()) notations.getChord(computedChord).stemDirection = computedStems.get(computedChord);
}
}
// notes displacement
NotesNotation notes = notesNotator.compute(chord, stemDirection, chordWidths, mc);
float leftSuspendedWidth = (notes.leftSuspended ? notes.noteheadWidthIs : 0);
// accidentals
AccidentalsNotation accs = accidentalsNotator.compute(chord, notes, chordWidths, mc);
// symbol's width: width of the noteheads and dots
float symbolWidth = notes.widthIs - leftSuspendedWidth;
float frontGap = accs.widthIs + leftSuspendedWidth;
// rear gap: empty duration-dependent space behind the chord minus the symbol's width
float rearGap = spacings.getWidth(chord.getDisplayedDuration()) - symbolWidth;
// lyric width
float lyricWidth = 0;
TextMeasurer textMeasurer = platformUtils().getTextMeasurer();
for (Lyric lyric : chord.getLyrics()) {
if (lyric != null && lyric.getText() != null) {
// width of lyric in interline spaces
FormattedText lyricText = styleText(lyric.getText(), new FormattedTextStyle(lyricsFont));
float l = lyricText.getWidth() / interlineSpace;
// for start and end syllable, request "-" more space, for middle syllables "--"
// TODO: unsymmetric - start needs space on the right, end on the left, ...
SyllableType lyricType = lyric.getSyllableType();
if (lyricType == SyllableType.Begin || lyricType == SyllableType.End) {
l += textMeasurer.measure(lyricsFont, "-").getWidth() / interlineSpace;
} else if (lyricType == SyllableType.Middle) {
l += textMeasurer.measure(lyricsFont, "--").getWidth() / interlineSpace;
}
// save width of the widest lyric
lyricWidth = Math.max(lyricWidth, l);
}
}
// compute length of the stem (if any)
float scaling = grace ? context.settings.scalingGrace : 1;
StemNotation stem = stemNotator.compute(chord.getStem(), notes.getLps(), stemDirection, context.mp.getStaff(), Companion.staffLines(mc.getLinesCount()), scaling);
// compute articulations
ArticulationsNotation arts = articulationsNotator.compute(chord, stemDirection, notes, mc.getLinesCount());
return new ChordNotation(chord, chord.getMP(), new ElementWidth(frontGap, symbolWidth, rearGap, lyricWidth), context.mp.getStaff(), notes, stemDirection, stem, accs, arts);
}
use of com.xenoage.zong.core.music.chord.StemDirection in project Zong by Xenoage.
the class OneMeasureOneStaff method compute.
public StemDirection[] compute(ChordLps[] chordsLps, int staffLinesCount) {
int staffMiddleLp = staffLinesCount - 1;
int upCount = 0;
int downCount = 0;
int furthestDistance = 0;
StemDirection furthestDistanceDir = Up;
// the chord with the note furthest away from the middle staff line
for (ChordLps chordLps : chordsLps) {
StemDirection preferredDir = singleStemDirector.compute(chordLps, staffLinesCount);
int distance;
if (preferredDir == Up) {
upCount++;
distance = staffMiddleLp - chordLps.getTop();
} else {
downCount++;
distance = chordLps.getBottom() - staffMiddleLp;
}
if (// new furthest distance found
distance > furthestDistance || // equal furthest distance found, but Down wins (Ross, p. 95):
(distance == furthestDistance && preferredDir == Down)) {
furthestDistance = distance;
furthestDistanceDir = preferredDir;
}
}
// the mostly used stem direction wins (Ross, p. 95)
// if both directions are equally distributed, the stem direction of
// the chord with the note furthest away from the staff middle line wins (Ross, p. 95)
StemDirection finalStemDir;
if (upCount != downCount)
finalStemDir = (upCount > downCount ? Up : Down);
else
finalStemDir = furthestDistanceDir;
// use same direction for all stems
StemDirection[] dirs = new StemDirection[chordsLps.length];
for (int i : range(dirs)) dirs[i] = finalStemDir;
return dirs;
}
use of com.xenoage.zong.core.music.chord.StemDirection in project Zong by Xenoage.
the class StemReader method read.
/**
* Reads and returns the stem of the given chord.
* If not available, {@link Stem#defaultStem} is returned.
* @param context the global context
* @param chord the chord, whose notes are already collected
* @param staff the staff index of the current chord
*/
public Stem read(Context context, Chord chord, int staff) {
if (mxlStem == null)
return Companion.getDefaultStem();
// direction
StemDirection direction = readStemDirection();
// length
Float length = null;
MxlPosition yPos = mxlStem.getPosition();
if (yPos.getDefaultY() != null) {
// convert position in tenths relative to topmost staff line into
// a length in interline spaces measured from the outermost chord note on stem side
float stemEndLinePosition = convertDefaultYToLP(context, yPos.getDefaultY(), staff);
VSide side = (direction == StemDirection.Up ? VSide.Top : VSide.Bottom);
length = Math.abs(stemEndLinePosition - getOuterNoteLp(context, chord, side, staff)) / 2;
}
// create stem
return new Stem(direction, length);
}
use of com.xenoage.zong.core.music.chord.StemDirection in project Zong by Xenoage.
the class ChordStamper method stampCore.
/**
* Draws the given chord, including noteheads, stem, flags, accidentals, dots,
* articulations and leger lines.
*/
public ChordStampings stampCore(ChordNotation chord, float chordXMm, StamperContext context) {
val staff = context.getCurrentStaffStamping();
Chord element = chord.element;
boolean grace = element.isGrace();
LayoutSettings settings = context.getSettings();
float scaling = (grace ? settings.scalingGrace : 1);
ChordWidths chordWidths = (grace ? settings.graceChordWidths : settings.chordWidths);
float leftNoteXMm = getLeftNoteXMm(chordXMm, chord.notes, staff.is);
// stem
StemStamping stem = stampStem(chord, leftNoteXMm, context);
// type of notehead
CommonSymbol noteheadSymbol = CommonSymbol.NoteWhole;
Duration.Type symbolType = Duration.INSTANCE.getNoteheadSymbolType(element.getDisplayedDuration());
if (symbolType == Duration.INSTANCE.Type.Half)
noteheadSymbol = CommonSymbol.NoteHalf;
else if (symbolType == Duration.INSTANCE.Type.Quarter)
noteheadSymbol = CommonSymbol.NoteQuarter;
// noteheads
NotesNotation notes = chord.notes;
NoteheadStamping[] noteheads = new NoteheadStamping[notes.getNotesCount()];
for (int iNote : range(noteheads)) {
NoteDisplacement note = notes.getNote(iNote);
Symbol noteSymbol = context.getSymbol(noteheadSymbol);
float noteXMm = getNoteheadXMm(leftNoteXMm + note.xIs * staff.is, scaling, staff, noteSymbol);
NoteheadStamping noteSt = new NoteheadStamping(chord, iNote, noteSymbol, Color.Companion.getBlack(), staff, sp(noteXMm, note.lp), scaling);
noteheads[iNote] = noteSt;
}
// flags (only drawn if there is no beam)
int flagsCount = Duration.INSTANCE.getFlagsCount(element.getDisplayedDuration());
Beam beam = element.getBeam();
StemDirection stemDir = chord.stemDirection;
FlagsStamping flags = null;
if (beam == null && flagsCount > 0 && chord.stem != null) /* can happen when no stem is used */
{
FlagsStamping.FlagsDirection flag = (stemDir == StemDirection.Up ? FlagsStamping.FlagsDirection.Down : FlagsStamping.FlagsDirection.Up);
Symbol flagSymbol = context.getSymbol(CommonSymbol.NoteFlag);
flags = new FlagsStamping(chord, staff, flag, flagsCount, flagSymbol, scaling, sp(leftNoteXMm + notes.stemOffsetIs * staff.is, chord.stem.endSlp.lp));
}
// accidentals
AccidentalsNotation accs = chord.accidentals;
AccidentalStamping[] accsSt = new AccidentalStamping[0];
if (accs != null) {
accsSt = new AccidentalStamping[accs.accidentals.length];
for (int iAcc : range(accsSt)) {
AccidentalDisplacement acc = accs.accidentals[iAcc];
AccidentalStamping accSt = new AccidentalStamping(chord, iAcc, staff, sp(chordXMm + (acc.xIs - chord.width.frontGap + 0.5f) * staff.is, acc.yLp), 1, context.getSymbol(CommonSymbol.getAccidental(acc.accidental)));
accsSt[iAcc] = accSt;
}
}
// dots
int[] dotPositions = notes.dotsLp;
int dotsPerNote = notes.getDotsPerNoteCount();
ProlongationDotStamping[] dots = new ProlongationDotStamping[dotPositions.length * dotsPerNote];
Symbol dotSymbol = context.getSymbol(CommonSymbol.NoteDot);
for (int iNote : range(dotPositions)) {
for (int iDot : range(dotsPerNote)) {
ProlongationDotStamping dotSt = new ProlongationDotStamping(chord, staff, dotSymbol, sp(leftNoteXMm + notes.getDotsOffsetIs(iDot) * staff.is, dotPositions[iNote]));
dots[iNote * dotsPerNote + iDot] = dotSt;
}
}
// articulations
ArticulationsNotation arts = chord.articulations;
ArticulationStamping[] artsSt = new ArticulationStamping[0];
if (arts != null) {
artsSt = new ArticulationStamping[arts.articulations.length];
float noteheadWidth = chordWidths.get(element.getDuration());
for (int iArt : range(artsSt)) {
ArticulationDisplacement art = arts.articulations[iArt];
ArticulationStamping artSt = new ArticulationStamping(chord, iArt, staff, sp(leftNoteXMm + (art.xIs + (noteheadWidth / 2)) * staff.is, art.yLp), 1, context.getSymbol(CommonSymbol.getArticulation(art.articulation)));
artsSt[iArt] = artSt;
}
}
// leger lines
LegerLineStamping[] legerLines = legerLinesStamper.stamp(chord, chordXMm, staff);
return new ChordStampings(element, chordXMm, staff, noteheads, dots, accsSt, legerLines, artsSt, flags, stem);
}
use of com.xenoage.zong.core.music.chord.StemDirection in project Zong by Xenoage.
the class SingleStaffBeamSlanter method computeClose.
/**
* Computes the slant for closely spaced beams.
*/
Slant computeClose(BeamedStems stems, StemDirection stemDir) {
// Ross, p. 112: beams in close spacing slant only 1/4 to 1/2 space
int dictatorLp = Math.round(stemDir == Up ? stems.getMaxNoteLp() : stems.getMinNoteLp());
Direction dir = (stems.rightNoteLp > stems.leftNoteLp ? Ascending : Descending);
// if dictator is on a staff line, use slant of 1/4 space
if (dictatorLp % 2 == 0 || abs(stems.rightNoteLp - stems.leftNoteLp) <= 1)
// on staff (Ross p. 112) or 2nd interval (Ross p. 111)
return slantIs(0.25f, dir);
else
return slantIs(0.5f, dir);
}
Aggregations