From 91ed53c20aeac61ce17998796f6219ad2490f2ca Mon Sep 17 00:00:00 2001 From: Marc Sabatella Date: Fri, 23 May 2014 20:51:46 -0600 Subject: [PATCH] fix #11622: spacing of grace notes with ledger lines --- libmscore/chord.cpp | 134 ++++++++++++++++++++++++++++++++++-------- mscore/actions.cpp | 2 +- vtest/gen | 2 +- vtest/gen.bat | 2 +- vtest/grace-3-ref.png | Bin 0 -> 9179 bytes vtest/grace-3.mscz | Bin 0 -> 1640 bytes 6 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 vtest/grace-3-ref.png create mode 100644 vtest/grace-3.mscz diff --git a/libmscore/chord.cpp b/libmscore/chord.cpp index e62c51ca44f75..e3af01487fb66 100644 --- a/libmscore/chord.cpp +++ b/libmscore/chord.cpp @@ -1689,13 +1689,31 @@ void Chord::layout() void Chord::layoutPitched() { - for (Chord* c : _graceNotes) - c->layoutPitched(); + int gi = 0; + for (Chord* c : _graceNotes) { + // HACK: graceIndex is not well-maintained on add & remove + // so rebuild now + c->setGraceIndex(gi++); + if (c->isGraceBefore()) + c->layoutPitched(); + } + QList graceNotesBefore; + int gnb = getGraceNotesBefore(&graceNotesBefore); + QList graceNotesAfter; + // lay out grace notes after separately so they are processed left to right + // (they are normally stored right to left) + int gna = getGraceNotesAfter(&graceNotesAfter); + if (gna) { + for (Chord* c : graceNotesAfter) + c->layoutPitched(); + } qreal _spatium = spatium(); qreal dotNoteDistance = score()->styleS(StyleIdx::dotNoteDistance).val() * _spatium; qreal minNoteDistance = score()->styleS(StyleIdx::minNoteDistance).val() * _spatium; qreal minTieLength = score()->styleS(StyleIdx::MinTieLength).val() * _spatium; + qreal ledgerLineLength = score()->styleD(StyleIdx::ledgerLineLength) * _spatium; + qreal graceMag = score()->styleD(StyleIdx::graceNoteMag); qreal chordX = (_noteType == NoteType::NORMAL) ? ipos().x() : 0.0; while (_ledgerLines) { @@ -1706,6 +1724,7 @@ void Chord::layoutPitched() qreal lll = 0.0; // space to leave at left of chord qreal rrr = 0.0; // space to leave at right of chord + qreal lhead = 0.0; // amount of notehead to left of chord origin Note* upnote = upNote(); delete _tabDur; // no TAB? no duration symbol! (may happen when converting a TAB into PITCHED) @@ -1740,6 +1759,8 @@ void Chord::layoutPitched() qreal x2 = x1 + note->headWidth(); lll = qMax(lll, -x1); rrr = qMax(rrr, x2); + // track amount of space due to notehead only + lhead = qMax(lhead, -x1); Accidental* accidental = note->accidental(); if (accidental) { @@ -1856,27 +1877,97 @@ void Chord::layoutPitched() if (_ledgerLines) { - // increase distance to previous chord if both have - // ledger lines + // we may need to increase distance to previous chord + Chord* pc = 0; + + if (_noteType == NOTE_NORMAL) { + // normal note + if (gnb) { + // if there are grace notes before, get last + pc = graceNotesBefore.last(); + } + else if (rtick()) { + // if this is not first chord of measure, get previous chord + Segment* s = segment()->prev(Segment::SegChordRest); + if (s && s->element(track()) && s->element(track())->type() == CHORD) + pc = static_cast(s->element(track())); + } + if (pc && !pc->graceNotes().isEmpty()) { + // if previous chord has grace notes after, find last one + // which, conveniently, is stored first + for (Chord* c : pc->graceNotes()) { + if (c->isGraceAfter()) { + pc = c; + break; + } + } + } + } + + else { + // grace note + Chord* mainChord = static_cast(parent()); + bool before = isGraceBefore(); + int incIdx = before ? -1 : 1; + int endIdx = before ? -1 : mainChord->graceNotes().size(); + // find previous grace note of same type + for (int i = _graceIndex + incIdx; i != endIdx; i += incIdx) { + Chord* pgc = mainChord->graceNotes().at(i); + if (pgc->isGraceBefore() == before) { + pc = pgc; + break; + } + } + if (!pc) { + // no previous grace note found + if (!before){ + // grace note after - use main note + pc = mainChord; + } + else if (mainChord->rtick()) { + // grace note before - use previous normal note of measure + Segment* s = mainChord->segment()->prev(Segment::SegChordRest); + if (s && s->element(track()) && s->element(track())->type() == CHORD) + pc = static_cast(s->element(track())); + } + } + } - Segment* s = segment(); - s = s->prev(Segment::SegChordRest); - if (s && s->element(track()) && s->element(track())->type() == ElementType::CHORD - && static_cast(s->element(track()))->ledgerLines()) { - // TODO: detect case where one chord is above staff, the other below - lll = qMax(_spatium * 0.8f, lll); + if (pc) { + // previous chord found + qreal llsp = 0.0; + int pUpLine = pc->upNote()->line(); + int pDownLine = pc->downNote()->line(); + int upLine = upNote()->line(); + int downLine = downNote()->line(); + int ledgerBelow = staff()->lines() * 2; + if (pc->_ledgerLines && ((pUpLine < 0 && upLine < 0) || (pDownLine >= ledgerBelow && downLine >= ledgerBelow))) { + // both chords have ledger lines above or below staff + llsp = ledgerLineLength; + // add space between ledger lines + llsp += _spatium * 0.2 * pc->mag(); + // if any portion of note extended to left of origin + // we need to include that here so it is not subsumed by ledger line + llsp += lhead; + } + else if (pc->up() && upLine < 0) { + // even if no ledger lines in previous chord, + // we may need a little space to avoid crossing stem + llsp = ledgerLineLength * 0.5; + llsp += _spatium * 0.2 * pc->mag(); + llsp += lhead; + } + lll = qMax(llsp, lll); } + } _space.setLw(lll); _space.setRw(rrr); - QList graceNotesBefore; - qreal graceMag = score()->styleD(StyleIdx::graceNoteMag); - int nb = getGraceNotesBefore(&graceNotesBefore); - if (nb){ + if (gnb){ qreal xl = -(_space.lw() + minNoteDistance) - chordX; - for (int i = nb-1; i >= 0; --i) { + for (int i = gnb-1; i >= 0; --i) { Chord* c = graceNotesBefore.value(i); xl -= c->space().rw()/* * 1.2*/; c->setPos(xl, 0); @@ -1885,10 +1976,7 @@ void Chord::layoutPitched() if (-xl > _space.lw()) _space.setLw(-xl); } - QList graceNotesAfter; - getGraceNotesAfter(&graceNotesAfter); - int na = graceNotesAfter.size(); - if (na){ + if (gna){ // get factor for start distance after main note. Values found by testing. qreal fc; switch (durationType().type()) { @@ -1902,7 +1990,7 @@ void Chord::layoutPitched() default: fc = 1; } qreal xr = fc * (_space.rw() + minNoteDistance); - for (int i = 0; i <= na - 1; i++) { + for (int i = 0; i <= gna - 1; i++) { Chord* c = graceNotesAfter.value(i); xr += c->space().lw() * (i == 0 ? 1.3 : 1); c->setPos(xr, 0); @@ -2638,7 +2726,7 @@ Measure* Chord::measure() const int Chord::getGraceNotesBefore(QList* graceNotesBefore) { int i = 0; - foreach (Chord* c, _graceNotes){ + foreach (Chord* c, _graceNotes) { if (c->noteType() == NoteType::ACCIACCATURA || c->noteType() == NoteType::APPOGGIATURA || c->noteType() == NoteType::GRACE4 @@ -2658,10 +2746,10 @@ int Chord::getGraceNotesBefore(QList* graceNotesBefore) int Chord::getGraceNotesAfter(QList* graceNotesAfter) { - if(_graceNotes.length() == 0) + if (_graceNotes.length() == 0) return 0; int i = 0; - for(int j = _graceNotes.length() - 1; j >= 0; --j){ + for (int j = _graceNotes.length() - 1; j >= 0; --j) { Chord* c = _graceNotes.at(j); if (c->noteType() == NoteType::GRACE8_AFTER || c->noteType() == NoteType::GRACE16_AFTER diff --git a/mscore/actions.cpp b/mscore/actions.cpp index b4bcd4a403a23..931ccc7509a01 100644 --- a/mscore/actions.cpp +++ b/mscore/actions.cpp @@ -1194,7 +1194,7 @@ Shortcut Shortcut::sc[] = { "grace8after", QT_TRANSLATE_NOOP("action","Grace: 8th after"), QT_TRANSLATE_NOOP("action","Add 8th grace note after"), - QT_TRANSLATE_NOOP("action","Grace: quarter"), + QT_TRANSLATE_NOOP("action","Grace: 8th after"), grace8after_ICON ), Shortcut( diff --git a/vtest/gen b/vtest/gen index 61b21cdfa62a1..3b31975a9088c 100755 --- a/vtest/gen +++ b/vtest/gen @@ -26,7 +26,7 @@ else chord-layout-7 chord-layout-8 chord-layout-9 chord-layout-10 chord-layout-11 chord-layout-12\ accidental-1 accidental-2 accidental-3 accidental-4\ accidental-5 accidental-6 accidental-7 accidental-8\ - tie-1 grace-1 grace-2 harmony-1 beams-1 beams-2 user-offset-1 user-offset-2" + tie-1 grace-1 grace-2 grace-3 harmony-1 beams-1 beams-2 user-offset-1 user-offset-2" fi DPI=130 diff --git a/vtest/gen.bat b/vtest/gen.bat index e4bcb61517eb2..15fe663ee8fae 100644 --- a/vtest/gen.bat +++ b/vtest/gen.bat @@ -13,7 +13,7 @@ set SRC=mmrest-1,bravura-mmrest,mmrest-2,mmrest-4,mmrest-5,mmrest-6,mmrest-7,mmr chord-layout-7,chord-layout-8,chord-layout-9,chord-layout-10,chord-layout-11,chord-layout-12, ^ accidental-1,accidental-2,accidental-3,accidental-4, ^ accidental-5,accidental-6,accidental-7,accidental-8, ^ - tie-1,grace-1,grace-2,harmony-1,beams-1,beams-2,user-offset-1,user-offset-2 + tie-1,grace-1,grace-2,grace-3,harmony-1,beams-1,beams-2,user-offset-1,user-offset-2 set MSCORE=..\win32install\bin\mscore.exe set DPI=130 diff --git a/vtest/grace-3-ref.png b/vtest/grace-3-ref.png new file mode 100644 index 0000000000000000000000000000000000000000..731722dad136caeefd57d397933daf4cd296583e GIT binary patch literal 9179 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 szbc;k!~UD`KmYidyMKQGfOu+wbo?iU@i