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

Scalable envelope graph #7194

Merged

Conversation

michaelgregorius
Copy link
Contributor

@michaelgregorius michaelgregorius commented Apr 5, 2024

This pull request makes the rendering of the AHDSR envelope graph scalable.

The painting code of the envelope is adjusted such that it does not assume fixed widths and heights anymore. The width of the lines and marker outlines is determined using the size of the widget so that it scales with the size. The fixed size of the widget has been replaced with a minimum size.

The markers are rendered as circles instead of rectangles because that looks much nicer especially when the widget is rendered at a larger size.

How does it look

The following shows a before and after for the same envelope:

EnvelopeGraphScalable-01-BeforeEnvelopeGraphScalable-02-After

The graph now provides three scaling modes that can be selected from a context menu:

  • "Dynamic": This modes corresponds to the rendering strategy of the previous implementation. 80/182 of the available width is initially used as the maximum width per segment. This can be interpreted like a "zoomed" version of the absolute mode. If the needed space becomes larger than the full width though then it falls back to relative rendering.
  • "Absolute": Each of the five segments is assigned 1/5 of the available width. The envelopes will always fit but might appear small depending of the current settings. This is a good mode to compare envelopes though.
  • "Relative": If there is at least one non-zero segment then the whole width is always used to present the envelope.

The default scaling mode is "Dynamic". The screenshot above shows an example of "Absolute" scaling.

Scaled rendering

Here's how a scaled version of the whole widget would look like. It's not possible to quickly demonstrate it in a nicer way because the instrument dialog does not really allow scaling even if layouts are used:

EnvelopeGraphScalable-03-ScaledUp

Implementation details

The new code is more or less divided into two parts. The first part calculates QPointF instances for the different points. In the second part these points are then used to draw the lines and markers. This makes the actual rendering code much more straight forward, readable and maintainable.

The interpolation between the line color of an inactive and an active envelope has also been restructured so that it is much more obvious that we are doing an interpolation in the first place. The colors at both ends of the interpolation are explicit now and can therefore be adjusted much easier. The actual color interpolation is done in the new helper function interpolateInRgb which might be moved elsewhere later if needed.

The line is rendered as a polyline instead of single line segments.

The drawing of the markers has been abstracted into a lambda (with some outside captures though) so that it can be easily adjusted if necessary.

A lerp function has been added to lmms_math.h.

Edit:

  • Updated the section about the three rendering modes

Remove the setting of a fixed size from the envelope graph and only set a minimum size.

Adjust the painting code of the envelope so that it does not assume fixed widths and heights anymore.

The new code is more or less divided into two parts. The first part calculates `QPointF` instances for the different points. In the second part these points are then used to draw the lines and markers. This makes the actual rendering code much more straight forward, readable and maintainable.

The interpolation between the line color of an inactive and an active envelope has also been restructured so that it is much more obvious that we are doing an interpolation in the first place. The colors at both ends of the interpolation are explicit now and can therefore be adjusted much easier. The actual color interpolation is done in the new helper function `interpolateInRgb` which might be moved elsewhere later if needed.

The line is rendered as a polyline instead of single line segments.

The drawing of the markers has been abstracted into a lambda (with some outside captures though) so that it can be easily adjusted if necessary. The markers are rendered as circles instead of rectangles because that looks much nicer especially when the widget is rendered at a larger size.

The width of the lines and marker outlines is determined using the size of the widget so that it scales with the size.

A `lerp` function has been added to `lmms_math.h`.
@michaelgregorius
Copy link
Contributor Author

@Veratil, @messmerd, this is the follow-up PR that I have mentioned in #7193.

@michaelgregorius
Copy link
Contributor Author

By the way @Veratil, it's the TabWidget that makes the layouts behave strange. If I replace it with a QTabWidget the layouts all work as intended and the widget takes the space it needs. The QTabWidget brings it's own problems though because the background of its children is not rendered in the colors set in the style sheet. It either seems to be a problem with the style sheet itself or with the palette stuff that LMMS does.

@Veratil
Copy link
Contributor

Veratil commented Apr 5, 2024

I believe it's this setting here in TabWidget.

Actually it might be its paintEvent

@michaelgregorius
Copy link
Contributor Author

I believe it's this setting here in TabWidget.

Commenting out that line unfortunately does not fix the problem. I think it's rather a problem with the QTabWidget, i.e. with the Qt version. Here's how it looks if I do not set any style for QTabWidget, its tab bar, etc.:

ProblemWithQTabWidget-NoStyleSheet

As you can see it does not inherit the background color from its parent widget.

And here's how it looks with a style applied that sets the background to the same dark color as for the other widgets:

ProblemWithQTabWidget-StyleSheetApplied

In this case it's still painted too bright.

I can "fix" the problem if I comment out the following line in src/gui/GuiApplication.cpp:

QApplication::setStyle(lmmsstyle);

Then it looks as follows:

ProblemWithQTabWidget-FixWithoutStyleSheet

Due to the strange interaction between the style sheet (which also defines a pseudo palette?) and the palette I am not really sure what's the real underlying cause of the problem though.

Add the two new scaling modes "Dynamic" and "Relative" so that there are three modes now:
* "Dynamic": This modes corresponds to the rendering strategy of the previous implementation. Initially 80/182 of the available width is used as the maximum width per segment. This can be interpreted like a "zoomed" version of the absolute mode. If the needed space becomes larger than the full width though then it falls back to relative rendering.
* "Absolute": Each of the five segments is assigned 1/5 of the available width. The envelopes will always fit but might appear small depending of the current settings. This is a good mode to compare envelopes though.
* "Relative": If there is at least one non-zero segment then the whole width is always used to present the envelope.

The default scaling mode is "Dynamic".

Add a context menu to the graph what can be used to set the current scaling mode.
@michaelgregorius
Copy link
Contributor Author

Here's a screenshot of the new context menu:

EnvelopeGraphScalable-04-ContextMenu

Move the function `interpolateInRgb` into the new class `ColorHelper` as it will later also be needed when the LFO graph is made scalable.
@michaelgregorius
Copy link
Contributor Author

Hi @Veratil, I'd appreciate if you could give this a final review.

src/gui/instrument/EnvelopeGraph.cpp Show resolved Hide resolved
src/gui/instrument/EnvelopeGraph.cpp Show resolved Hide resolved
src/gui/instrument/EnvelopeGraph.cpp Outdated Show resolved Hide resolved
src/gui/instrument/EnvelopeGraph.cpp Outdated Show resolved Hide resolved
src/gui/instrument/EnvelopeGraph.cpp Outdated Show resolved Hide resolved
src/gui/instrument/EnvelopeGraph.cpp Outdated Show resolved Hide resolved
src/gui/instrument/EnvelopeGraph.cpp Outdated Show resolved Hide resolved
src/gui/instrument/EnvelopeGraph.cpp Outdated Show resolved Hide resolved
src/gui/instrument/EnvelopeGraph.cpp Outdated Show resolved Hide resolved
@michaelgregorius michaelgregorius merged commit 8e40038 into LMMS:master Apr 11, 2024
9 checks passed
@michaelgregorius michaelgregorius deleted the EnvelopeGraphDynamicallyRendered branch April 11, 2024 15:50
enp2s0 pushed a commit to enp2s0/lmms that referenced this pull request Apr 12, 2024
Make the graph scalable by adjusting the painting code of the envelope so that it does not assume fixed widths and heights anymore. Remove the setting of a fixed size from the envelope graph and only set a minimum size.

Make three scaling modes available which can be selected via a context menu in the graph:
* "Dynamic": This modes corresponds to the rendering strategy of the previous implementation. Initially 80/182 of the available width is used as the maximum width per segment. This can be interpreted like a "zoomed" version of the absolute mode. If the needed space becomes larger than the full width though then it falls back to relative rendering.
* "Absolute": Each of the five segments is assigned 1/5 of the available width. The envelopes will always fit but might appear small depending of the current settings. This is a good mode to compare envelopes though.
* "Relative": If there is at least one non-zero segment then the whole width is always used to present the envelope.

The default scaling mode is "Dynamic".

## Technical details

The new painting code is more or less divided into two parts. The first part calculates `QPointF` instances for the different points. In the second part these points are then used to draw the lines and markers. This makes the actual rendering code much more straight forward, readable and maintainable.

The interpolation between the line color of an inactive and an active envelope has also been restructured so that it is much more obvious that we are doing an interpolation in the first place. The colors at both ends of the interpolation are explicit now and can therefore be adjusted much easier. The actual color interpolation is done in the helper function `interpolateInRgb` which is provided by the new class `ColorHelper`. This class will later also be needed when the LFO graph is made scalable.

The line is rendered as a polyline instead of single line segments.

The drawing of the markers has been abstracted into a lambda (with some outside captures though) so that it can be easily adjusted if necessary. The markers are rendered as circles instead of rectangles because that looks much nicer especially when the widget is rendered at a larger size.

The width of the lines and marker outlines is determined using the size of the widget so that it scales with the size.

A `lerp` function has been added to `lmms_math.h`.
@Rossmaxx
Copy link
Contributor

Was looking at old issues and saw this mockup by musikBear #3067 (comment)

I am thinking the merged plugin toolbar might look good with your concept and will help with scaling. Wanted to bring this to your attention

@michaelgregorius
Copy link
Contributor Author

Was looking at old issues and saw this mockup by musikBear #3067 (comment)

I am thinking the merged plugin toolbar might look good with your concept and will help with scaling. Wanted to bring this to your attention

Thanks for the info @Rossmaxx! I have added a comment to #2510 with a remark that the design has become a possibility now that the envelope and LFO graph are scalable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants