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

Add an option to separate layout of similarly stemmed voices #23001

Merged
merged 1 commit into from
Jun 5, 2024

Conversation

miiizen
Copy link
Contributor

@miiizen miiizen commented May 28, 2024

This PR introduces a toggle to disable the current behaviour when laying out chords, which is to combine similarly stemmed chords into one large chord in order to calculate note placement, notehead mirroring and dot layout.
There is a score wide option in styles and a per-voice option in the properties panel.
Screenshot 2024-06-03 at 10 33 51
Screenshot 2024-06-03 at 10 34 52

Screen.Recording.2024-06-03.at.10.37.33.mov

@miiizen miiizen force-pushed the chord-voice-layout branch 4 times, most recently from c27c4f9 to ca2a581 Compare June 3, 2024 09:32
@miiizen miiizen marked this pull request as ready for review June 3, 2024 09:41
@miiizen miiizen requested a review from oktophonie June 4, 2024 07:28
@oktophonie oktophonie requested a review from mike-spa June 4, 2024 07:49
Copy link
Contributor

@mike-spa mike-spa left a comment

Choose a reason for hiding this comment

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

At some point we'll have to take the courage to refactor all of these methods. For now, good job!

@@ -2032,17 +2048,25 @@ double ChordLayout::layoutChords2(std::vector<Note*>& notes, bool up, LayoutCont
// to track mirror status of previous note
bool isLeft = notes[startIdx]->chord()->up(); // is notehead on left?
staff_idx_t lStaffIdx = notes[startIdx]->chord()->vStaffIdx(); // staff of last note (including staffMove)
track_idx_t lTrack = notes[startIdx]->chord()->track();
bool lCombine = notes[startIdx]->chord()->combineVoice();
Copy link
Contributor

Choose a reason for hiding this comment

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

I know you're following the naming style that was already there, but I didn't know what lTrack meant until I noticed the comment on the previous line. Always better to use readable names. Also, I find the naming lastSomething misleading, as it doesn't mean the actual last but the previous one in the loop, so perhaps prevSomething is clearer.

Fraction tick = notes.front()->chord()->segment()->tick();
double sp = staff->spatium(tick);
double stepDistance = sp * staff->lineDistance(tick) * .5;
int stepOffset = staff->staffType(tick)->stepOffset();

double upDotPosX = 0.0;
double downDotPosX = 0.0;
std::array<double, 12> dotPos{};
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't quite understand why this has size 12. Is it because it's 3 * VOICES? Better express it in terms of VOICES and perhaps a comment to give it some context

Note* above = (i < nNotes - 1) ? notes[i + 1] : nullptr;
if (above
&& (!above->visible() || above->dotsHidden() || above->chord()->dots() == 0 || !chord->combineVoice()
|| !above->chord()->combineVoice())) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I see lots of instances where you're evaluating conditions like chord1->combineVoice() && chord2->combineVoice() or its negation. You could probably benefit from introducing a method in the Chord class for this. It could be something like
bool combineVoice(const Chord* otherChord) const { return combineVoice() && otherChord->combineVoice(); }.
Or actually even better as a static method:
static bool combineVoice(const Chord* chord1, const Chord* chord2) { return chord1->combineVoice() && chord2->combineVoice(); }
and maybe tag it as inline to make it zero-cost.
That would make these things a bit more readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are 2 && and 2 || comparisons so far - I'm tempted to leave them as is. I think static combineVoiceAnd and combineVoiceOr functions would be overkill

Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need two: the || comparison here is just the negation of the && comparison. In boolean logic !(a && b) == !a || !b. So if you define
static bool Chord::combineVoices(Chord* chord1, Chord* chord2) { return chord1->combineVoice() && chord2->combineVoice(); },
then above you will call combineVoices(chord1, chord2) and here you will call !combineVoices(chord1, chord2), which makes it clearer what this means and easier to read :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤦 sorry, demorgen's rolling in his grave - sorted!

continue;
}
chord->setDotPosX(maxPosX);
const double maxPosX = std::max(upDotPosX, downDotPosX);
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this part be moved out into its own function, perhaps, or would that complicate your life? In general, all of these ChordLayout functions are extremely hard to read, but there's nothing we can do about it really, when our starting point was functions with 500 LOC and 25 levels of indentation.

A day may come when we rewrite this whole thing...
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, sorted this section out and will return to tidy the other parts of the function another day...

@mike-spa mike-spa merged commit e1efd7a into musescore:master Jun 5, 2024
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Development

Successfully merging this pull request may close these issues.

3 participants