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

Collisions overhaul (and other improvements to horizontal spacing) #11099

Merged
merged 5 commits into from
Apr 14, 2022

Conversation

mike-spa
Copy link
Contributor

@mike-spa mike-spa commented Apr 5, 2022

This PR rewrites a substantial part of horizontal spacing, particularly the way we perform collision checks. Most notably (and finally) now items can tuck (or kern) above/below each other.

In more detail, what I did is:

  1. Removed the logic within the code that was preventing elements from kerning.
  2. Implemented a system which calculates the spacing at "high resolution", meaning it considers every single graphical piece of an item (notehead, stem, hook, ledger lines, etc...).
  3. Managed several exceptions and special cases related to kerning (basically, cases when items should not kern).
  4. Refactored the interaction of all the user-defined spacing settings (specifically those from Style -> Measure).
  5. Corrected a small error related to the spacing of polyrythms.
  6. Corrected mid-system clefs positioning.
  7. Implemented a smarter way of deciding how many measures fit in one system. (I had tried this already in previous PRs but it was breaking on edge cases. Now it is finally stable.)

A side benefit of allowing items to tuck is that we save a huge amount of horizontal space that we were previously wasting, which lets us:
8. Increase the default value of minNoteDistance to a much more reasonable 0.6 spaces.
This is done in a separate commit, as it requires to also change all template files.

Basically, every single visual test is expected to fail, and I've usually had also utests failing on beams, so that's also to be expected.

@MarcSabatella if I may bother you, it'd be great if you could link some of the old issues that will be fixed by this (one of which you mentioned already). Thanks for the amazing support!

Before:
Screenshot from 2022-04-01 12-35-00

After:
Screenshot from 2022-04-01 12-36-27

Before:
Screenshot from 2022-04-05 17-13-49
Screenshot from 2022-04-05 17-12-48

After:
Screenshot from 2022-04-05 17-05-42

@MarcSabatella
Copy link
Contributor

This is fantastic! Happy to update the issues once this is merged. But, I did want to verify something. When I took a crck at fixing this a couple of years ago, the issue I ran into was that at the point where we were calculating the spacing requirements for a given note, we didn't yet know for sure if the previous note was upstem or downstem if it was beamed. That's because beam directions weren't computer until later. So in any case where the initially guessed stem direction was up, but the actual stem direction turned out to be down, we would make the wrong call regarding the tucking. See for instance my examples in #4784

I can totally believe this might not be an issue any \more because the beam code has been rewritten. But I wouldn't mind hearing confirmation.

// be squeezed. I'm temporarily rolling back the idea (still a bit too risky in some edge cases) [M.S.]
double acceptanceRange = 1;
bool doBreak = (system->measures().size() > 1) && ((curSysWidth + ww) > systemWidth * acceptanceRange);
double acceptanceRange = 0.3 * system->squeezableSpace();
Copy link
Contributor

Choose a reason for hiding this comment

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

Where does the 0.3 come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nothing, it just looks good. May be worth a comment though, thanks.

@@ -745,6 +745,26 @@ void LayoutMeasure::getNextMeasure(const LayoutOptions& options, LayoutContext&
}

LayoutBeams::createBeams(score, ctx, measure);
/* HACK: The real beam layout is computed at much later stage (you can't do the beams until you know
Copy link
Contributor

Choose a reason for hiding this comment

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

@MarcSabatella This may be the answer to your question

@ecstrema
Copy link
Contributor

ecstrema commented Apr 6, 2022

Just to be sure: are these vtests correct?

Lyrics 6-1
image

Here the note order changed. Is it expected?
image

changed file?
image

No lyrics under timesig:
image

(multiple of these)
image

weird
image

and
image

I have probably missed some more.

@ecstrema
Copy link
Contributor

ecstrema commented Apr 6, 2022

One tiny question: Is there any performance overhead for this more complex layout algorithm?

@mike-spa
Copy link
Contributor Author

mike-spa commented Apr 6, 2022

This is fantastic! Happy to update the issues once this is merged. But, I did want to verify something. When I took a crck at fixing this a couple of years ago, the issue I ran into was that at the point where we were calculating the spacing requirements for a given note, we didn't yet know for sure if the previous note was upstem or downstem if it was beamed. That's because beam directions weren't computer until later. So in any case where the initially guessed stem direction was up, but the actual stem direction turned out to be down, we would make the wrong call regarding the tucking. See for instance my examples in #4784

I can totally believe this might not be an issue any \more because the beam code has been rewritten. But I wouldn't mind hearing confirmation.

@MarcSabatella stems were a real head scratcher. It turned out that now direction was known at the point of horizontal spacing, but extension was not. And yes, @Marr11317 pointed out the answer. Not an elegant solution at all (and I may look for a better one in future) but one that seems to do the job.

@mike-spa
Copy link
Contributor Author

mike-spa commented Apr 6, 2022

@Marr11317 Clefs/timesigs/keysigs are an exception to kerning, meaning they shouldn't kern above/below other items. Lyrics are the exception to the exception to kerning. Gonna look into that.

@mike-spa mike-spa force-pushed the elementsCanTuck branch 6 times, most recently from 13b7758 to 16847fc Compare April 11, 2022 17:26
@@ -4098,7 +4101,8 @@ void Measure::computeWidth(Segment* s, qreal x, bool isSystemHeader, Fraction mi
bool first = isFirstInSystem();
const Shape ls(first ? RectF(0.0, -1000000.0, 0.0, 2000000.0) : RectF(0.0, 0.0, 0.0, spatium() * 4));

qreal minNoteSpace = score()->noteHeadWidth() + score()->styleMM(Sid::minNoteDistance);
static constexpr double spacingMultiplier = 1.2;
qreal minNoteSpace = score()->noteHeadWidth() + spacingMultiplier * score()->styleMM(Sid::minNoteDistance);
Copy link
Contributor

Choose a reason for hiding this comment

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

If this spacingMultiplier is only used there, why not modify Sid::minNoteDistance instead?

@@ -5401,6 +5405,160 @@ void Score::doLayoutRange(const Fraction& st, const Fraction& et)
}
}

void Score::createPaddingTable()
{
double minimumPaddingUnit = 0.1 * spatium();
Copy link
Contributor

Choose a reason for hiding this comment

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

another magic number. Why not 0.01?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a hard limit on the distance that two items can come within; it only applies in certain special situations, e.g. polyrhythms between notes in two voices where the normal minNoteDistance does not apply. 0.01 would be too small, but like all the other settings this is to become user-configurable eventually.

}
}

const double ledgerPad = 0.25 * spatium();
Copy link
Contributor

Choose a reason for hiding this comment

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

should'nt that ledger padding come from the score's configured staff weight?

* but we introduce an appropriate exception for same-voice cases in Shape::minHorizontalDistance().
*/
_paddingTable[ElementType::NOTE][ElementType::NOTE] = minimumPaddingUnit;
_paddingTable[ElementType::NOTE][ElementType::LEDGER_LINE] = 0.35 * spatium();
Copy link
Contributor

Choose a reason for hiding this comment

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

dito

_paddingTable[ElementType::STEM][ElementType::STEM] = 0.85 * spatium();
_paddingTable[ElementType::STEM][ElementType::ACCIDENTAL] = 0.35 * spatium();
_paddingTable[ElementType::STEM][ElementType::LEDGER_LINE] = 0.35 * spatium();
_paddingTable[ElementType::LEDGER_LINE][ElementType::STEM] = 0.35 * spatium();
Copy link
Contributor

Choose a reason for hiding this comment

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

All these magic numbers may need to be modified with other music fonts.

Did you just set these values because you tried them?

If yes, you tried them with Leland I guess?

Copy link
Contributor

Choose a reason for hiding this comment

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

The magic numbers emanate from me (@mike-spa working here under my brutal and unforgiving instruction).

In the end we want all the values of the padding table to be user-configurable. We can't do that right now as there's a high design cost and it will need a lot of thought to work out how to expose them all, since there are so many values and potential interactions between them.

For now we're taking the settings that do already exist (in the Style -> Measure/Bar dialog) and using them in the relevant slots in the table, and then deriving other values from them where possible. For the remainder I have supplied defaults for the time being, which aren't arbitrary.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good then. It can always be made configurable when users ask for it I guess.

@@ -513,6 +513,9 @@ class Score : public QObject, public EngravingObject
void assignIdIfNeed(Staff& staff) const;
void assignIdIfNeed(Part& part) const;

PaddingTable _paddingTable;
void createPaddingTable();
Copy link
Contributor

Choose a reason for hiding this comment

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

is the padding table updated when the user changes score properties?

@mike-spa mike-spa force-pushed the elementsCanTuck branch 2 times, most recently from b2d48c3 to 4f505a8 Compare April 12, 2022 14:01
@oktophonie oktophonie added the vtests This PR produces approved changes to vtest results label Apr 12, 2022
@Jojo-Schmitz
Copy link
Contributor

"8 - engraving_utests (SEGFAULT)"

@mike-spa
Copy link
Contributor Author

"8 - engraving_utests (SEGFAULT)"

Yeah I saw that. I never had that kind failure: how can I replicate that specific test on my machine to debug it?

@Jojo-Schmitz
Copy link
Contributor

Not sure at all. There are other failures too, diffs. Maybe those in the end result in a crash?

@mike-spa
Copy link
Contributor Author

Yeah I'm now going through the diffs and fix those, then I'll force push again and let's see what happens

Complete overhaul of horizontal spacing collision management. Items are finally allowed to kern above/below each other.
and changed in all the templates
@mike-spa
Copy link
Contributor Author

@igorkorsukov I got a compiler error after the last rebase related to a QList vs std conflict. I hope I've fixed it correctly

@vpereverzev vpereverzev merged commit ff75767 into musescore:master Apr 14, 2022
Comment on lines +143 to +146
// These are necessary to be sure that ALL the layout is correctly updated when loading any file
masterScore->styleChanged();
masterScore->update();

Copy link
Contributor

Choose a reason for hiding this comment

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

@mike-spa can we remove this block of code?
This code takes a lot of time when opening a big score.
You can test on the score from this PR #8143

This code does not solve all the problems with the long opening, but it significantly reduces the time.
And the comment says that this code may not be executed, we just make sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Eism I had to do that because many scores were not loading the new spacing settings correctly, so for now we can't just remove the code, unfortunately. But there is probably a smarter way to do it, I can try to look into that. Just for my understanding: which one of them is more time consuming? styleChanged()or update()?

Side note: #8143 was opened before this PR was merged, so there must be also something else going on, I guess.

Copy link
Contributor

Choose a reason for hiding this comment

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

Score::update

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.

Excellent, thank you. I'll try to look into it as soon as I can!

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.

None yet

7 participants