Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/engraving/dom/chordrest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,10 @@ void ChordRest::undoChangeProperty(Pid id, const PropertyValue& newValue, Proper
{
if (id == Pid::BEAM_MODE) {
if (isChord() && m_durationType.hooks() == 0) {
return;
const StaffType* tab = staffType();
if (!tab || !tab->isTabStaff() || !tab->genDurations()) {
return;
}
}
BeamMode newBeamMode = newValue.value<BeamMode>();
if ((newBeamMode == BeamMode::BEGIN16 && m_durationType.hooks() < 2)
Expand Down
4 changes: 4 additions & 0 deletions src/engraving/rendering/score/chordlayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3461,6 +3461,10 @@ void ChordLayout::fillShape(const Chord* item, ChordRest::LayoutData* ldata)
shape.add(note->shape().translate(note->pos()));
}

if (item->tabDur() && item->tabDur()->addToSkyline()) {
shape.add(item->tabDur()->shape().translate(item->tabDur()->pos()));
}

for (const NoteParenthesisInfo* parenInfo : item->noteParentheses()) {
Parenthesis* leftParen = parenInfo->leftParen();
Parenthesis* rightParen = parenInfo->rightParen();
Expand Down
4 changes: 4 additions & 0 deletions src/engraving/rendering/score/systemlayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,10 @@ void SystemLayout::layoutSystemElements(System* system, LayoutContext& ctx)
GraceNotesGroup& graceAfter = chord->graceNotesAfter();
TLayout::layoutGraceNotesGroup2(&graceBefore, graceBefore.mutldata());
TLayout::layoutGraceNotesGroup2(&graceAfter, graceAfter.mutldata());
if (TabDurationSymbol* symbol = chord->tabDur()) {
TLayout::layoutTabDurationSymbol2(symbol, symbol->mutldata());
ChordLayout::fillShape(chord, chord->mutldata());
}
}

for (ChordRest* cr : elementsToLayout.chordRests) {
Expand Down
56 changes: 44 additions & 12 deletions src/engraving/rendering/score/tlayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
#include "dom/factory.h"

#include "editing/editchord.h"
#include "editing/navigation.h"

#include "accidentalslayout.h"
#include "arpeggiolayout.h"
Expand Down Expand Up @@ -5683,11 +5684,32 @@ void TLayout::layoutTabDurationSymbol(const TabDurationSymbol* item, TabDuration
double xpos, ypos; // position coords

ldata->beamGrid = TabBeamGrid::NONE;
ldata->beamLength = 0.0;
ldata->beamLevel = 0;
Chord* chord = item->ownershipParent() && item->ownershipParent()->isChord() ? toChord(item->ownershipParent()) : nullptr;
// A grid replaces the individual duration signs. Only connect equal, undotted
// values; otherwise retain the signs rather than lose their rhythm information.
auto connects = [](const ChordRest* left, const ChordRest* right) {
if (!left || !right || !left->isChord() || !right->isChord()) {
return false;
}
const bool regularChords = !left->isGrace() && !right->isGrace()
&& !toChord(left)->noStem() && !toChord(right)->noStem();
const bool sameContext = left->measure() == right->measure() && left->staffType() == right->staffType()
&& left->tuplet() == right->tuplet();
const bool equalUndottedDurations = left->durationType() == right->durationType() && !left->dots() && !right->dots();
const bool leftContinues = left->beamMode() == BeamMode::BEGIN || left->beamMode() == BeamMode::MID;
const bool rightConnects = right->beamMode() == BeamMode::MID || right->beamMode() == BeamMode::END;
return regularChords && sameContext && equalUndottedDurations && leftContinues && rightConnects;
};
const int beamLevel = chord ? static_cast<int>(chord->durationType().type())
- static_cast<int>(item->tab()->tabDurationFont().zeroBeamLevel) : 0;
const bool joinsPrevious = beamLevel > 0 && (chord->beamMode() == BeamMode::MID || chord->beamMode() == BeamMode::END)
&& connects(Navigation::prevChordRest(chord), chord);
const bool joinsNext = beamLevel > 0 && (chord->beamMode() == BeamMode::BEGIN || chord->beamMode() == BeamMode::MID)
&& connects(chord, Navigation::nextChordRest(chord));
// if no chord (shouldn't happens...) or not a special beam mode, layout regular symbol
if (!chord || !chord->isChord()
|| (chord->beamMode() != BeamMode::BEGIN && chord->beamMode() != BeamMode::MID
&& chord->beamMode() != BeamMode::END)) {
if (beamLevel < 1 || (!joinsPrevious && !joinsNext)) {
FontMetrics fm(item->tab()->durationFont());
hbb = item->tab()->durationBoxH();
wbb = fm.width(item->text());
Expand All @@ -5710,15 +5732,8 @@ void TLayout::layoutTabDurationSymbol(const TabDurationSymbol* item, TabDuration
ybb = -hbb; // bbox top is at top of stem height
xpos = 0.75 * spatium; // conventional centring of stem on fret marks
ypos = item->tab()->durationGridYOffset(); // stem start is at bottom
if (chord->beamMode() == BeamMode::BEGIN) {
ldata->beamGrid = TabBeamGrid::INITIAL;
ldata->beamLength = 0.0;
} else if (chord->beamMode() == BeamMode::MID || chord->beamMode() == BeamMode::END) {
ldata->beamLevel = (static_cast<int>(chord->durationType().type()) - static_cast<int>(font.zeroBeamLevel));
ldata->beamGrid = (ldata->beamLevel < 1 ? TabBeamGrid::INITIAL : TabBeamGrid::MEDIALFINAL);
// _beamLength and bbox x and width will be set in layout2(),
// once horiz. positions of chords are known
}
ldata->beamLevel = beamLevel;
ldata->beamGrid = joinsPrevious ? TabBeamGrid::MEDIALFINAL : TabBeamGrid::INITIAL;
}
// set this' mag from parent chord mag (include staff mag)
double mag = chord != nullptr ? chord->mag() : 1.0;
Expand All @@ -5729,6 +5744,23 @@ void TLayout::layoutTabDurationSymbol(const TabDurationSymbol* item, TabDuration
ldata->setPos(xpos * mag, ypos * mag);
}

void TLayout::layoutTabDurationSymbol2(const TabDurationSymbol* item, TabDurationSymbol::LayoutData* ldata)
{
if (ldata->beamGrid != TabBeamGrid::MEDIALFINAL) {
return;
}
const ChordRest* previous = Navigation::prevChordRest(toChord(item->ownershipParent()));
if (!previous || !previous->tabDur()) {
return;
}
// Horizontal spacing is now final. Drawing applies the symbol magnification again.
const double length = previous->tabDur()->pagePos().x() - item->pagePos().x();
ldata->beamLength = length / item->magS();
RectF bbox = ldata->bbox();
bbox.setLeft(std::min(bbox.left(), length));
ldata->setBbox(bbox);
}

void TLayout::layoutTapping(Tapping* item, Tapping::LayoutData* ldata, LayoutContext& ctx)
{
TappingLayout::layoutTapping(item, ldata, ctx);
Expand Down
1 change: 1 addition & 0 deletions src/engraving/rendering/score/tlayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ class TLayout
static void layoutSystemText(const SystemText* item, SystemText::LayoutData* ldata);

static void layoutTabDurationSymbol(const TabDurationSymbol* item, TabDurationSymbol::LayoutData* ldata);
static void layoutTabDurationSymbol2(const TabDurationSymbol* item, TabDurationSymbol::LayoutData* ldata);
static void layoutTapping(Tapping* item, Tapping::LayoutData* ldata, LayoutContext& ctx);
static void layoutTappingHalfSlur(TappingHalfSlur* item);
static void layoutTempoText(const TempoText* item, TempoText::LayoutData* ldata);
Expand Down
90 changes: 90 additions & 0 deletions src/engraving/tests/beam_data/historical-tab-grid.mscx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?xml version='1.0' encoding='utf-8'?>
<museScore version="5.00">
<Score>
<Division>480</Division>
<Style>
<pageWidth>8.27</pageWidth>
<pageHeight>11.69</pageHeight>
<spatium>1.76389</spatium>
</Style>
<Part id="1">
<Staff>
<StaffType group="tablature">
<lines>6</lines>
<lineDistance>1.5</lineDistance>
<stemless>1</stemless>
<timesig>0</timesig>
<durations>1</durations>
<durationFontName>MuseScore Tab Italian</durationFontName>
<durationFontSize>15</durationFontSize>
<durationFontY>0</durationFontY>
<fretUseTextStyle>0</fretUseTextStyle>
<fretPresetIdx>0</fretPresetIdx>
<fretFontSize>9</fretFontSize>
<fretFontY>0</fretFontY>
<linesThrough>0</linesThrough>
<minimStyle>1</minimStyle>
<onLines>1</onLines>
<showRests>0</showRests>
<stemsDown>1</stemsDown>
<stemsThrough>0</stemsThrough>
<upsideDown>0</upsideDown>
<useNumbers>1</useNumbers>
<showBackTied>1</showBackTied>
<symbolRepeat>3</symbolRepeat>
</StaffType>
</Staff>
<Instrument id="cavaquinho">
<trackName>Renaissance Tenor Lute (6 course)</trackName>
<minPitchP>43</minPitchP>
<maxPitchP>79</maxPitchP>
<minPitchA>43</minPitchA>
<maxPitchA>79</maxPitchA>
<instrumentId>pluck.guitar</instrumentId>
<StringData>
<frets>24</frets>
<string>43</string>
<string>48</string>
<string>53</string>
<string>57</string>
<string>62</string>
<string>67</string>
</StringData>
<Channel>
<program value="25" />
<synti>Fluid</synti>
</Channel>
</Instrument>
</Part>
<Staff id="1">
<Measure>
<voice>
<TimeSig>
<sigN>2</sigN>
<sigD>4</sigD>
</TimeSig>
<Chord>
<BeamMode>auto</BeamMode>
<durationType>quarter</durationType>
<Note>
<pitch>67</pitch>
<tpc>15</tpc>
<fret>0</fret>
<string>0</string>
</Note>
</Chord>
<Chord>
<BeamMode>auto</BeamMode>
<durationType>quarter</durationType>
<Note>
<pitch>69</pitch>
<tpc>17</tpc>
<fret>2</fret>
<string>0</string>
</Note>
</Chord>
</voice>
</Measure>
</Staff>
</Score>
</museScore>
73 changes: 72 additions & 1 deletion src/engraving/tests/beam_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/

#include <gtest/gtest.h>
#include <memory>

#include "engraving/dom/beam.h"
#include "engraving/dom/chord.h"
Expand All @@ -30,11 +31,14 @@
#include "engraving/dom/note.h"
#include "engraving/dom/tremolotwochord.h"

#include "engraving/editing/editbeam.h"
#include "engraving/editing/editdata.h"
#include "engraving/editing/flip.h"
#include "engraving/editing/navigation.h"
#include "engraving/editing/transaction/transaction.h"

#include "utils/scorerw.h"
#include "utils/scorecomp.h"
#include "utils/scorerw.h"

using namespace mu::engraving;

Expand All @@ -61,6 +65,73 @@ void Engraving_BeamTests::beam(const char* path)
delete score;
}

TEST_F(Engraving_BeamTests, historicalTabGridModes)
{
std::unique_ptr<MasterScore> score(ScoreRW::readScore(BEAM_DATA_DIR + u"historical-tab-grid.mscx"));
ASSERT_TRUE(score);
ChordRest* first = score->firstMeasure()->firstChordRest(0);
ASSERT_TRUE(first);
ChordRest* second = Navigation::nextChordRest(first);
ASSERT_TRUE(second);
ASSERT_EQ(first->durationType().type(), DurationType::V_QUARTER);
ASSERT_EQ(second->durationType().type(), DurationType::V_QUARTER);

auto setMode = [&](ChordRest* cr, BeamMode mode) {
score->select(cr, SelectType::SINGLE);
score->transactionManager()->transaction(TranslatableString::untranslatable("Engraving beam tests"), [&](Transaction& tx) {
EditBeam::setBeamMode(tx, score.get(), mode);
});
};
setMode(first, BeamMode::BEGIN);
ASSERT_EQ(first->beamMode(), BeamMode::BEGIN);
setMode(second, BeamMode::MID);
ASSERT_EQ(second->beamMode(), BeamMode::MID);

EditData ed;
score->undoRedo(true, &ed);
EXPECT_EQ(second->beamMode(), BeamMode::AUTO);
score->undoRedo(true, &ed);
EXPECT_EQ(first->beamMode(), BeamMode::AUTO);
score->undoRedo(false, &ed);
score->undoRedo(false, &ed);
EXPECT_EQ(first->beamMode(), BeamMode::BEGIN);
EXPECT_EQ(second->beamMode(), BeamMode::MID);

ASSERT_TRUE(ScoreRW::saveScore(score.get(), u"historicalTabGridModes.mscx"));
std::unique_ptr<MasterScore> reopened(ScoreRW::readScore(u"historicalTabGridModes.mscx", true));
ASSERT_TRUE(reopened);
ChordRest* restored = Navigation::nextChordRest(reopened->firstMeasure()->firstChordRest(0));
ASSERT_TRUE(restored);
EXPECT_EQ(restored->beamMode(), BeamMode::MID);

// These modes subdivide modern beams and still require actual hooks.
setMode(second, BeamMode::BEGIN16);
EXPECT_EQ(second->beamMode(), BeamMode::MID);
setMode(second, BeamMode::BEGIN32);
EXPECT_EQ(second->beamMode(), BeamMode::MID);

score->deselectAll();
score->transactionManager()->transaction(TranslatableString::untranslatable("Engraving beam tests"), [&](Transaction& tx) {
EditBeam::resetBeamMode(tx, score.get());
});
EXPECT_EQ(first->beamMode(), BeamMode::AUTO);
EXPECT_EQ(second->beamMode(), BeamMode::AUTO);
score->undoRedo(true, &ed);
EXPECT_EQ(first->beamMode(), BeamMode::BEGIN);
EXPECT_EQ(second->beamMode(), BeamMode::MID);
score->undoRedo(false, &ed);
EXPECT_EQ(first->beamMode(), BeamMode::AUTO);
EXPECT_EQ(second->beamMode(), BeamMode::AUTO);

score.reset(ScoreRW::readScore(u"selectionfilter_data/selectionfilter1.mscx"));
ASSERT_TRUE(score);
ChordRest* quarter = score->firstMeasure()->firstChordRest(0);
ASSERT_TRUE(quarter);
ASSERT_EQ(quarter->durationType().type(), DurationType::V_QUARTER);
setMode(quarter, BeamMode::BEGIN);
EXPECT_EQ(quarter->beamMode(), BeamMode::AUTO);
}

TEST_F(Engraving_BeamTests, beamA)
{
beam("Beam-A.mscx");
Expand Down
Binary file added vtest/scores/historical-tab-grid-boundaries.mscz
Binary file not shown.
Binary file not shown.
Binary file added vtest/scores/historical-tab-grids.mscz
Binary file not shown.
Loading