use of com.xenoage.zong.musiclayout.layouter.scoreframelayout.util.ChordStampings in project Zong by Xenoage.
the class TupletStamper method createTupletStamping.
/**
* Computes the {@link TupletStamping} for the given {@link ChordStampings}
* and returns it.
*/
public TupletStamping createTupletStamping(Tuplet tuplet, OpenTupletsCache cache, SymbolPool symbolPool) {
StaffStamping ss = cache.getChord(tuplet.getChords().get(0), tuplet).staff;
// horizontal position of the bracket
float xDistance = ss.is / 4;
float x1Mm = cache.getChord(tuplet.getFirstChord(), tuplet).xMm - xDistance;
ChordStampings cs2 = cache.getChord(tuplet.getLastChord(), tuplet);
// TODO: notehead width!
float cs2Width = ss.is * 1.2f;
float x2Mm = cs2.xMm + cs2Width + xDistance;
// vertical position of the bracket (above or below) depends on the directions
// of the majority of the stems
int stemDir = 0;
for (Chord chord : tuplet.getChords()) {
ChordStampings cs = cache.getChord(chord, tuplet);
if (cs.stem != null) {
stemDir += cs.stem.direction.getSign();
}
}
// 1: above, -1: below
int placement = (stemDir < 0 ? 1 : -1);
// compute position of start and end point
// by default, the bracket is 1.5 IS away from the end of the stems
// when there is no stem, the innermost notehead is used
// TODO: if stems of inner chords are longer, correct!
float distanceLp = 1.5f * 2;
float y1Lp = computeBracketLP(cache.getChord(tuplet.getFirstChord(), tuplet), placement, distanceLp);
float y2Lp = computeBracketLP(cache.getChord(tuplet.getLastChord(), tuplet), placement, distanceLp);
// at least 2 IS over top barline / under bottom barline
if (// above staff
placement == 1) {
y1Lp = Math.max(y1Lp, (ss.linesCount - 1) * 2 + 4);
y2Lp = Math.max(y1Lp, (ss.linesCount - 1) * 2 + 4);
} else // below staff
{
y1Lp = Math.min(y1Lp, 0 - 4);
y2Lp = Math.min(y1Lp, 0 - 4);
}
// text
float fontSize = 10 * ss.is / 1.6f;
FormattedText text = createText(tuplet.getActualNotes(), fontSize, symbolPool);
// return result
return new TupletStamping(sp(x1Mm, y1Lp), sp(x2Mm, y2Lp), true, text, ss);
}
use of com.xenoage.zong.musiclayout.layouter.scoreframelayout.util.ChordStampings 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.musiclayout.layouter.scoreframelayout.util.ChordStampings in project Zong by Xenoage.
the class ChordStamper method stampAll.
/**
* Returns all the stampings for the given {@link Chord}, including beams,
* tuplets, slurs and other attachments.
*
* The given {@link OpenSlursCache},
* {@link OpenLyricsCache}, {@link LastLyrics} and {@link OpenTupletsCache} may be modified.
*/
public List<Stamping> stampAll(ChordNotation chord, float xMm, BeamSpacing beam, StaffStampings staffStampings, StamperContext context, FormattedTextStyle defaultLyricStyle, OpenSlursCache openSlursCache, OpenLyricsCache openLyricsCache, LastLyrics lastLyrics, OpenTupletsCache openTupletsCache) {
List<Stamping> ret = alist();
Chord element = chord.getElement();
int staffIndex = context.staffIndex;
int systemIndex = context.systemIndex;
// noteheads, leger lines, dots, accidentals, stem, flags, articulations
ChordStampings chordSt = stampCore(chord, xMm, context);
chordSt.addAllTo(ret);
// beam
if (beam != null) {
// stamp the whole beam (when we find the beginning of the beam)
// TIDY: create/store beam stampings elsewhere?
Beam beamElement = beam.notation.element;
int chordIndex = beamElement.getWaypointIndex(element);
if (chordIndex == 0) {
ret.addAll(beamStamper.stamp(beam, context.getCurrentStaffStamping()));
}
}
// ties and slurs
for (Slur slur : element.getSlurs()) {
SlurWaypoint wp = slur.getWaypoint(element);
WaypointPosition pos = slur.getWaypointPosition(element);
// TODO: choose top/bottom
int noteIndex = notNull(wp.getNoteIndex(), 0);
NoteheadStamping notehead = chordSt.noteheads[noteIndex];
// define the placement: above or below (TODO: better strategy)
VSide side = slurStamper.getSide(slur);
// compute position
val staff = staffStampings.get(systemIndex, notehead.parentStaff.staffIndex);
val slurCache = openSlursCache.getOrCreate(slur);
float distanceIs = slurStamper.getAdditionalDistanceIs(chord, slur.getSide());
SP defaultSp = sp(notehead.position.xMm, notehead.position.lp + side.getDir() * distanceIs * 2);
if (pos == WaypointPosition.Start)
slurCache.setStart(defaultSp, staff, systemIndex);
else
slurCache.setStop(defaultSp, staff, systemIndex);
}
// lyric
List<Lyric> lyrics = element.getLyrics();
if (lyrics.size() > 0) {
float baseLine = -10;
for (Lyric lyric : lyrics) {
if (lyric != null) {
SyllableType lyricType = lyric.getSyllableType();
StaffTextStamping lastLyric = lastLyrics.get(staffIndex, lyric.getVerse());
if (lyricType == SyllableType.Extend) {
// extend
if (// TODO: frame breaks...
lastLyric != null) {
// remember it
openLyricsCache.setUnderscore((Lyric) lastLyric.getElement(), lastLyric, chordSt.noteheads[0], /* TODO*/
staffIndex);
}
} else {
// normal lyric
// create text stamping
StaffTextStamping sts = lyricStamper.createSyllableStamping(lyric, defaultLyricStyle, context.getCurrentStaffStamping(), chordSt.noteheads[0].position.xMm, baseLine);
ret.add(sts);
// when middle or end syllable, add a hypen between the preceding syllable and this syllable
if (// TODO: frame breaks...
lastLyric != null) {
if (lyricType == SyllableType.Middle || lyricType == SyllableType.End) {
StaffTextStamping hyphenStamping = lyricStamper.createHyphenStamping(lastLyric, sts, defaultLyricStyle);
ret.add(hyphenStamping);
}
}
// remember this lyric as the currently last one in the current staff and verse
lastLyrics.set(staffIndex, lyric.getVerse(), sts);
}
}
baseLine += -5;
}
}
// directions
ret.addAll(directionStamper.stampForChord(chordSt, context.layouter.symbols));
// tuplet
Tuplet tuplet = element.getTuplet();
if (tuplet != null) {
openTupletsCache.addChord(element, tuplet, chordSt);
}
return ret;
}
Aggregations