Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New cross staff beam positions #23529

Merged
merged 5 commits into from
Jul 11, 2024
Merged

Conversation

miiizen
Copy link
Contributor

@miiizen miiizen commented Jul 9, 2024

Resolves: #21901

This PR implements correct cross staff beam placement for beams between staves and also above & below staves.
Screenshot 2024-07-11 at 11 41 21

Pointer & dictator must reflect a beam's flatness in offsetBeamToRemoveCollisions.
Calculate the middle line on the correct stave, and get this in the correct number of quarter spaces.
@oktophonie oktophonie added the vtests This PR produces approved changes to vtest results label Jul 11, 2024
@oktophonie oktophonie requested a review from mike-spa July 11, 2024 10:48
@@ -1133,6 +1140,45 @@ enum class LayoutFlag : char {
};

typedef muse::Flags<LayoutFlag> LayoutFlags;

struct ChordPosition {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would NotePosition be a more descriptive name here? It seems to be mostly connected to notes. Also, given that this type is very specific of the Beam code and isn't used anywhere else, it probably doesn't need to be defined here. I think you could move this definition to beam.h or beambase.h, or if you like even put it inside the Beam (or BeamBase) class. Then, methods of the Beam class can refer to it as ChordPosition, while methods outside the Beam class (like BeamLayout) can refer to it as Beam::ChordPosition, which I think is elegant and also makes its meaning clearer (otherwise the meaning of this class is not super clear being here out of context).

@@ -356,6 +356,13 @@ enum class BeamMode : signed char {
END
};

enum class CrossStaffBeamPosition : signed char {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same reasoning here: this could probably be defined inside BeamBase or even inside BeamBase::LayoutData

bool secondToLastNoteSameHeightAsLowerEnd = endNote > startNote && ldata->elements[chordCount - 2]->isChord()
&& toChord(
ldata->elements[chordCount - 2])->downLine() == lowerEnd;
Chord* secondNote = ldata->elements[1]->isChord() ? toChord(ldata->elements[1]) : nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be better to define a variable earlier to avoid so many repeated calls to ldata->elements

std::set<int> noteSet(item->notes().begin(), item->notes().end());
std::vector<int> notes(noteSet.begin(), noteSet.end());
std::set<ChordPosition> noteSet(item->notes().begin(), item->notes().end());
std::vector<int> notes;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen this elsewhere (and I think it's due to legacy), but it looks very weird to define a vector of ints and call it notes. Perhaps noteLines?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also good to do here notes.reserve(noteSet.size) since you already know the size you'll need

std::vector<int> notes;
std::transform(std::begin(noteSet), std::end(noteSet), std::back_inserter(notes), [] (const ChordPosition& pos) {
return pos.line;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an old skool for loop is simpler to read (and shorter, too)

for (ChordPosition pos : noteSet) {
    notes.pushBack(pos.line)
}

|| ((topSlantDir != 0 && topSlantDir != overallDirection)
|| (bottomSlantDir != 0 && bottomSlantDir != overallDirection)
|| (beamSideSwitchDirection != 0 && beamSideSwitchDirection != overallDirection)
|| (staffSwitchDirection != 0 && staffSwitchDirection != overallDirection));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to avoid these big lists of conditions if you can. Sometimes breaking these down into intermediate bool variables helps make it more readable and simpler to debug


// Calculate slant between notes on stave closest to the beam
int closestChordsSize = closestChordsToBeam.size();
ChordPosition closestStartPos(closestChordsSize > 0 && closestChordsToBeam.front() ? closestChordsToBeam.front()->line() : 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think null-checking closestChordsToBeam.front() and .back() is redundant here

@mike-spa mike-spa merged commit 14f1447 into musescore:master Jul 11, 2024
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
vtests This PR produces approved changes to vtest results
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Correct beam slants and endpoints for new cross-stave positions
3 participants