Skip to content

Commit 9a78414

Browse files
committed
Avoid collisions in full tab layout
1 parent fdf6726 commit 9a78414

File tree

6 files changed

+70
-30
lines changed

6 files changed

+70
-30
lines changed

src/engraving/dom/note.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,10 @@ double Note::bboxRightPos() const
10021002
//---------------------------------------------------------
10031003
double Note::headBodyWidth() const
10041004
{
1005+
const StaffType* st = staffType();
1006+
if (st && st->isTabStaff()) {
1007+
return tabHeadWidth(st);
1008+
}
10051009
return headWidth() + 2 * bboxXShift();
10061010
}
10071011

@@ -3806,4 +3810,9 @@ bool Note::negativeFretUsed() const
38063810
{
38073811
return engravingConfiguration()->negativeFretsAllowed() && m_fret < 0;
38083812
}
3813+
3814+
int Note::stringOrLine() const
3815+
{
3816+
return staff()->staffType(tick())->isTabStaff() ? string() * 2 : line();
3817+
}
38093818
}

src/engraving/dom/note.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ class Note final : public EngravingItem
272272
bool negativeFretUsed() const;
273273
int string() const { return m_string; }
274274
void setString(int val) { m_string = val; }
275+
int stringOrLine() const;
275276

276277
bool ghost() const { return m_ghost; }
277278
void setGhost(bool val) { m_ghost = val; }

src/engraving/rendering/dev/chordlayout.cpp

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,6 +1466,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
14661466
const track_idx_t startTrack = staffIdx * VOICES;
14671467
const track_idx_t endTrack = startTrack + VOICES;
14681468
const Fraction tick = segment->tick();
1469+
const StaffType* staffType = staff->staffType(segment->tick());
14691470

14701471
// we need to check all the notes in all the staves of the part so that we don't get weird collisions
14711472
// between accidentals etc with moved notes
@@ -1487,7 +1488,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
14871488
std::vector<Note*> downStemNotes;
14881489
int upVoices = 0;
14891490
int downVoices = 0;
1490-
double nominalWidth = ctx.conf().noteHeadWidth() * staff->staffMag(tick);
1491+
double nominalWidth = !isTab ? ctx.conf().noteHeadWidth() * staff->staffMag(tick) : ctx.conf().fretWidth(staffType);
14911492
double maxUpWidth = 0.0;
14921493
double maxDownWidth = 0.0;
14931494
double maxUpMag = 0.0;
@@ -1542,7 +1543,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
15421543
}
15431544
}
15441545

1545-
if (upVoices + downVoices && !isTab) {
1546+
if (upVoices + downVoices && staffType->stemThrough()) {
15461547
// TODO: use track as secondary sort criteria?
15471548
// otherwise there might be issues with unisons between voices
15481549
// in some corner cases
@@ -1553,7 +1554,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
15531554
// layout upstem noteheads
15541555
if (upVoices > 1) {
15551556
std::sort(upStemNotes.begin(), upStemNotes.end(),
1556-
[](Note* n1, const Note* n2) ->bool { return n1->line() > n2->line(); });
1557+
[](Note* n1, const Note* n2) ->bool { return n1->stringOrLine() > n2->stringOrLine(); });
15571558
}
15581559
if (upVoices) {
15591560
double hw = layoutChords2(upStemNotes, true, ctx);
@@ -1563,7 +1564,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
15631564
// layout downstem noteheads
15641565
if (downVoices > 1) {
15651566
std::sort(downStemNotes.begin(), downStemNotes.end(),
1566-
[](Note* n1, const Note* n2) ->bool { return n1->line() > n2->line(); });
1567+
[](Note* n1, const Note* n2) ->bool { return n1->stringOrLine() > n2->stringOrLine(); });
15671568
}
15681569
if (downVoices) {
15691570
double hw = layoutChords2(downStemNotes, false, ctx);
@@ -1640,7 +1641,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
16401641
if (upVoices && downVoices) {
16411642
Note* bottomUpNote = upStemNotes.front();
16421643
Note* topDownNote = downStemNotes.back();
1643-
int separation = topDownNote->line() - bottomUpNote->line();
1644+
int separation = topDownNote->stringOrLine() - bottomUpNote->stringOrLine();
16441645

16451646
std::vector<Note*> overlapNotes;
16461647
overlapNotes.reserve(8);
@@ -1666,21 +1667,21 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
16661667

16671668
// build list of overlapping notes
16681669
for (size_t i = 0, n = upStemNotes.size(); i < n; ++i) {
1669-
if (upStemNotes[i]->line() >= topDownNote->line() - 1) {
1670+
if (upStemNotes[i]->stringOrLine() >= topDownNote->stringOrLine() - 1) {
16701671
overlapNotes.push_back(upStemNotes[i]);
16711672
} else {
16721673
break;
16731674
}
16741675
}
16751676
for (size_t i = downStemNotes.size(); i > 0; --i) { // loop most probably needs to be in this reverse order
1676-
if (downStemNotes[i - 1]->line() <= bottomUpNote->line() + 1) {
1677+
if (downStemNotes[i - 1]->stringOrLine() <= bottomUpNote->stringOrLine() + 1) {
16771678
overlapNotes.push_back(downStemNotes[i - 1]);
16781679
} else {
16791680
break;
16801681
}
16811682
}
16821683
std::sort(overlapNotes.begin(), overlapNotes.end(),
1683-
[](Note* n1, const Note* n2) ->bool { return n1->line() > n2->line(); });
1684+
[](Note* n1, const Note* n2) ->bool { return n1->stringOrLine() > n2->stringOrLine(); });
16841685

16851686
// determine nature of overlap
16861687
bool shareHeads = true; // can all overlapping notes share heads?
@@ -1704,7 +1705,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
17041705
continue;
17051706
}
17061707
}
1707-
int line = n->line();
1708+
int line = n->stringOrLine();
17081709
int d = lastLine - line;
17091710
switch (d) {
17101711
case 0:
@@ -1776,8 +1777,8 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
17761777
int topDownStemLen = 0;
17771778
if (!conflictUnison && topDownNote->chord()->stem()) {
17781779
topDownStemLen = std::round(topDownNote->chord()->stem()->ldata()->bbox().height() / sp * 2);
1779-
if (bottomUpNote->line() > firstLedgerBelow - 1 && topDownNote->line() < bottomUpNote->line()
1780-
&& topDownNote->line() + topDownStemLen >= firstLedgerBelow) {
1780+
if (bottomUpNote->stringOrLine() > firstLedgerBelow - 1 && topDownNote->stringOrLine() < bottomUpNote->stringOrLine()
1781+
&& topDownNote->stringOrLine() + topDownStemLen >= firstLedgerBelow) {
17811782
ledgerOverlapBelow = true;
17821783
}
17831784
}
@@ -1786,8 +1787,8 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
17861787
int bottomUpStemLen = 0;
17871788
if (!conflictUnison && bottomUpNote->chord()->stem()) {
17881789
bottomUpStemLen = std::round(bottomUpNote->chord()->stem()->ldata()->bbox().height() / sp * 2);
1789-
if (topDownNote->line() < -1 && topDownNote->line() < bottomUpNote->line()
1790-
&& bottomUpNote->line() - bottomUpStemLen <= firstLedgerAbove) {
1790+
if (topDownNote->stringOrLine() < -1 && topDownNote->stringOrLine() < bottomUpNote->stringOrLine()
1791+
&& bottomUpNote->stringOrLine() - bottomUpStemLen <= firstLedgerAbove) {
17911792
ledgerOverlapAbove = true;
17921793
}
17931794
}
@@ -1841,8 +1842,8 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
18411842
} else {
18421843
// Prevent ledger line & notehead collision
18431844
double adjSpace
1844-
= (topDownNote->line() <= firstLedgerAbove
1845-
|| bottomUpNote->line() >= firstLedgerBelow) ? ledgerLen - ledgerGap - 0.2 * sp : -0.2 * sp;
1845+
= (topDownNote->stringOrLine() <= firstLedgerAbove
1846+
|| bottomUpNote->stringOrLine() >= firstLedgerBelow) ? ledgerLen - ledgerGap - 0.2 * sp : -0.2 * sp;
18461847
upOffset = maxDownWidth + adjSpace;
18471848
if (downHooks) {
18481849
bool needsHookSpace = (ledgerOverlapBelow || ledgerOverlapAbove);
@@ -1854,21 +1855,23 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
18541855
} else {
18551856
// no direct conflict, so parts can overlap (downstem on left)
18561857
// just be sure that stems clear opposing noteheads and ledger lines
1857-
double clearLeft = 0.0, clearRight = 0.0;
1858+
// Stems are in the middle of fret marks on TAB staves
1859+
double clearLeft = isTab ? bottomUpNote->chord()->stemPosX() : 0.0;
1860+
double clearRight = isTab ? bottomUpNote->chord()->stemPosX() : 0.0;
18581861
if (topDownNote->chord()->stem()) {
18591862
if (ledgerOverlapBelow) {
18601863
// Create space between stem and ledger line below staff
1861-
clearLeft = ledgerLen + ledgerGap + topDownNote->chord()->stem()->lineWidth();
1864+
clearLeft += ledgerLen + ledgerGap + topDownNote->chord()->stem()->lineWidth();
18621865
} else {
1863-
clearLeft = topDownNote->chord()->stem()->lineWidth() + 0.3 * sp;
1866+
clearLeft += topDownNote->chord()->stem()->lineWidth() + 0.3 * sp;
18641867
}
18651868
}
18661869
if (bottomUpNote->chord()->stem()) {
18671870
if (ledgerOverlapAbove) {
18681871
// Create space between stem and ledger line above staff
1869-
clearRight = maxDownWidth + ledgerLen + ledgerGap - maxUpWidth + bottomUpNote->chord()->stem()->lineWidth();
1872+
clearRight += maxDownWidth + ledgerLen + ledgerGap - maxUpWidth + bottomUpNote->chord()->stem()->lineWidth();
18701873
} else {
1871-
clearRight = bottomUpNote->chord()->stem()->lineWidth() + std::max(maxDownWidth - maxUpWidth, 0.0) + 0.3 * sp;
1874+
clearRight += bottomUpNote->chord()->stem()->lineWidth() + std::max(maxDownWidth - maxUpWidth, 0.0) + 0.3 * sp;
18721875
}
18731876
} else {
18741877
downDots = 0; // no need to adjust for dots in this case
@@ -1878,15 +1881,18 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
18781881
Note* topUpNote = upStemNotes.back();
18791882
// Move notes out of the way of straight flags
18801883
int pad = ctx.conf().styleB(Sid::useStraightNoteFlags) ? 2 : 1;
1881-
bool overlapsFlag = topDownNote->line() + topDownStemLen + pad > topUpNote->line();
1884+
bool overlapsFlag = topDownNote->stringOrLine() + topDownStemLen + pad > topUpNote->stringOrLine();
18821885
if (downHooks && (ledgerOverlapBelow || overlapsFlag)) {
18831886
// we will need more space to avoid collision with hook
18841887
// but we won't need as much dot adjustment
1888+
Hook* hook = topDownNote->chord()->hook();
1889+
double hookWidth = hook ? hook->width() : 0.0;
18851890
if (ledgerOverlapBelow) {
1886-
Hook* hook = topDownNote->chord()->hook();
1887-
double hookWidth = hook ? hook->width() : 0.0;
18881891
upOffset = hookWidth + ledgerLen + ledgerGap;
18891892
}
1893+
if (isTab) {
1894+
upOffset = hookWidth + maxDownWidth;
1895+
}
18901896
upOffset = std::max(upOffset, maxDownWidth + 0.1 * sp);
18911897
dotAdjustThreshold = maxUpWidth - 0.3 * sp;
18921898
}
@@ -1937,6 +1943,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
19371943
Chord::LayoutData* chordLdata = chord->mutldata();
19381944
if (chord->up()) {
19391945
if (!muse::RealIsNull(upOffset)) {
1946+
oversizeUp = isTab ? oversizeUp / 2 : oversizeUp;
19401947
chordLdata->moveX(upOffset + centerAdjustUp + oversizeUp);
19411948
if (downDots && !upDots) {
19421949
chordLdata->moveX(dotAdjust);
@@ -1967,7 +1974,7 @@ void ChordLayout::layoutChords1(LayoutContext& ctx, Segment* segment, staff_idx_
19671974
}
19681975
if (upVoices + downVoices > 1) {
19691976
std::sort(notes.begin(), notes.end(),
1970-
[](Note* n1, const Note* n2) ->bool { return n1->line() > n2->line(); });
1977+
[](Note* n1, const Note* n2) ->bool { return n1->stringOrLine() > n2->stringOrLine(); });
19711978
}
19721979
layoutChords3(ctx.conf().style(), chords, notes, staff, ctx);
19731980
}
@@ -2019,9 +2026,17 @@ double ChordLayout::layoutChords2(std::vector<Note*>& notes, bool up, LayoutCont
20192026

20202027
for (int idx = startIdx; idx != endIdx; idx += incIdx) {
20212028
Note* note = notes[idx]; // current note
2022-
int line = note->line(); // line of current note
2029+
const int line = note->stringOrLine(); // line of current note
20232030
Chord* chord = note->chord();
2024-
staff_idx_t staffIdx = chord->vStaffIdx(); // staff of current note
2031+
const staff_idx_t staffIdx = chord->vStaffIdx(); // staff of current note
2032+
const Staff* st = note->staff();
2033+
const StaffType* tab = st->staffTypeForElement(note);
2034+
const bool isTab = note->staff() && note->staff()->isTabStaff(note->chord()->tick());
2035+
2036+
if (isTab) {
2037+
// Need to lay out TAB note to know width
2038+
TLayout::layoutNote(note, note->mutldata());
2039+
}
20252040

20262041
// there is a conflict
20272042
// if this is same or adjacent line as previous note (and chords are on same staff!)
@@ -2069,7 +2084,8 @@ double ChordLayout::layoutChords2(std::vector<Note*>& notes, bool up, LayoutCont
20692084

20702085
// accumulate return value
20712086
if (!mirror) {
2072-
maxWidth = std::max(maxWidth, note->bboxRightPos());
2087+
const double noteWidth = isTab ? note->tabHeadWidth(tab) : note->bboxRightPos();
2088+
maxWidth = std::max(maxWidth, noteWidth);
20732089
}
20742090

20752091
// prepare for next iteration
@@ -2274,7 +2290,7 @@ void ChordLayout::placeDots(const std::vector<Chord*>& chords, const std::vector
22742290
}
22752291
}
22762292
}
2277-
if (!chord || chord->staff()->isTabStaff(chord->tick())) {
2293+
if (!chord || (chord->staff()->isTabStaff(chord->tick()) && !chord->staff()->staffType(chord->tick())->stemThrough())) {
22782294
return;
22792295
}
22802296
std::vector<Note*> topDownNotes;
@@ -3448,7 +3464,8 @@ void ChordLayout::layoutNote2(Note* item, LayoutContext& ctx)
34483464
double correctMag = item->chord()->notes().size() > 1 ? item->chord()->mag() : item->mag();
34493465
double d = ctx.conf().point(ctx.conf().styleS(Sid::dotNoteDistance)) * correctMag;
34503466
double dd = ctx.conf().point(ctx.conf().styleS(Sid::dotDotDistance)) * correctMag;
3451-
double x = item->chord()->dotPosX() - item->pos().x() - item->chord()->pos().x();
3467+
double x = isTabStaff ? item->chord()->dotPosX() - item->pos().x() : item->chord()->dotPosX() - item->pos().x()
3468+
- item->chord()->pos().x();
34523469
// in case of dots with different size, center-align them
34533470
if (item->mag() != item->chord()->mag() && item->chord()->notes().size() > 1) {
34543471
double relativeMag = item->mag() / item->chord()->mag();

src/engraving/rendering/dev/layoutcontext.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ bool LayoutConfiguration::isPrintingMode() const
7373
return score()->printing();
7474
}
7575

76+
double LayoutConfiguration::fretWidth(const StaffType* tab) const
77+
{
78+
if (!tab || !tab->isTabStaff()) {
79+
return 0.0;
80+
}
81+
82+
muse::draw::Font f = tab->fretFont();
83+
f.setPointSizeF(tab->fretFontSize());
84+
return muse::draw::FontMetrics::width(f, u"0");
85+
}
86+
7687
std::shared_ptr<const IEngravingFont> LayoutConfiguration::engravingFont() const
7788
{
7889
IF_ASSERT_FAILED(score()) {

src/engraving/rendering/dev/layoutcontext.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "../../style/style.h"
3232
#include "../../iengravingfont.h"
3333
#include "../../dom/mscore.h"
34+
#include "../../dom/stafftype.h"
3435

3536
#include "../layoutoptions.h"
3637

@@ -103,6 +104,7 @@ class LayoutConfiguration
103104

104105
bool isShowVBox() const { return options().isShowVBox; }
105106
double noteHeadWidth() const { return options().noteHeadWidth; }
107+
double fretWidth(const StaffType* tab) const;
106108
bool isShowInvisible() const;
107109
int pageNumberOffset() const;
108110
bool isVerticalSpreadEnabled() const;

src/engraving/rendering/dev/tlayout.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4313,7 +4313,7 @@ void TLayout::layoutNote(const Note* item, Note::LayoutData* ldata)
43134313
const_cast<Note*>(item)->setHeadHasParentheses(false, /* addToLinked= */ false);
43144314
}
43154315

4316-
double w = item->tabHeadWidth(tab); // !! use _fretString
4316+
double w = item->tabHeadWidth(tab);
43174317
double mags = item->magS();
43184318

43194319
const MStyle& style = item->style();

0 commit comments

Comments
 (0)