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 vector graph implementation #7158

Open
wants to merge 184 commits into
base: master
Choose a base branch
from
Open

New vector graph implementation #7158

wants to merge 184 commits into from

Conversation

szeli1
Copy link
Contributor

@szeli1 szeli1 commented Mar 23, 2024

This pull request adds a new widget to LMMS called VectorGraph.
This widget has been implemented in the WaveShaper effect.
A new effect called "FFTFilter" was planned but removed from the final pull request.

I consider this pull request finished.

There was an other vector graph implementation before this in pr #4367.

This new widget does not have a fixed size (unlike Graph.h) and uses points for the base graph instead of samples. This allows better workflow and smoother output. The lines (and samples) between points are automatically generated in paintEvent. The graph supports outputs with custom sample count. The graph supports different types of lines such as "sine", "peak", "steps", "random". The graph also supports automatable point attributes and interaction between two graphs inside the same widget (called "effector graphs" in the code).
The changes are compatible with older versions.

How to use:
Press somewhere in the widget with left click to add a new point.
Delete points by pressing right click and dragging over them.
Click a point with left click to select it. A small menu will show up. From there you can open up an editing window where almost all of the point's attributes are editable. VectorGraphDataArray (the green or white waveform / graph) can block access to some attribute settings if setup correctly.
In the editing menu there is a knob called "automation knob". This knob is used only for automation. The user can set which attribute is automated with a combo box labeled "automated attribute". Automated points will show up with a different color.
The user can switch which VectorGraphDataArray (the green or white waveform / graph) is edited by clicking a point and pressing the "switch graph" option. The number in the top left corner shows the currently edited VectorGraphDataArray.
By double left clicking on a point, an input dialog shows up which can edit the x and y position of the point.
The "1. attribute value" and the "2. attribute value" setting is used different ways in different line types, the line type can be selected in the editing window.
How one graph effects the other can be changed in the editing window with the effect settings. In WaveShaper these settings allow the green graph to be effected by the white graph. These settings allow easier automation and different interactions between the 2 graph such as "add" and "clamp". Use these effects by selecting a point on the green graph (in this case) and in the editing window select an effect from the combo box labeled "1. effect". The user can specify if they want to apply the effect to a point's attribute or to the whole line (to each sample) by clicking the "effect line" or "effect point" buttons.

Why is the white line / waveform there?

The white line can interact whit the green line. The user can decide how the lines interact by selecting different interaction "effects" in the editing window. These effects include "add" (the white line will be added to the green, (the lines range from -1 to 1)) and "clamp upper" (the white line will act as an upper ceiling to the green line). The user can decide between the white line effecting only points or only samples or points and samples. This means for example that the white line will only be added to the green line's points or it will only be added to the green line's samples or both. In the future the white graph can be used for example to display a signal processed with FFT. In that case the green graph could react in complex ways to the white graph without using automation.

(In this video the output is the red (green) graph and the input is the blue (white))
https://github.com/user-attachments/assets/247045ce-da4e-4348-9db8-9a9a6ab59c7d

Different implementations can give different restrictions to different VectorGraphDataArrays.
The second VectorGraphDataArray (the white waveform / graph) is easily removable in the code.

TODO list:

  • rewrite comments

  • rename functions and values

  • test all the features / settings

  • do TODO-s

  • automation:

  • -- add automation support:

  • -- save FloatModels

  • -- safe deletion (currently not safe??) needs testing

  • event when a dataArray's size gets to 0

  • scaled graph option in VectorGraphView which scales the last used output to be displayed in the widget (does not call getValues (renamed to getSamples)). need testing

  • call dataChanged() in setDataArray

  • journalling in PointGraphModel

  • setDataArray keep attributes option, formatArray option which runs formatArray

  • context menu in gui (clear automation, connect to controller)

  • display hints (full text) while editing

  • ability to edit multiple graphs using m_isLastSelectedArray

  • handle effector arrays when deleting VectorGraphDataArray

  • update formatArray

  • licensing email

  • new button in gui which switches between the currently selected graphs

  • add new effects: clamp

  • dragging

  • redo clamps

  • add bezier

  • rename getUpdatingFromEffector() input

  • separeate m_isAutomatableEffectable

  • deallocate m_universalSampleBuffer ?

  • use ComboBox::DEFAULT_HEIGHT

  • add comments in VectorGraphViewBase

  • implement Model::dataChanged

  • filter implementation

  • remove qDebug

Final WaveShaper VectorGraph implementation:
Finished10
Finished11
Finished12
Different VectorGraphDataArrays effecting each other, one is automated:
Finished13
Finished14

Keep in mind that this widget can have harmful bugs/errors.
Feel free to make suggestions and criticize my coding style / decisions.

Test file:
vectorgraph_tests2.mmp.txt

@szeli1 szeli1 marked this pull request as draft March 23, 2024 20:32
@szeli1 szeli1 marked this pull request as ready for review April 18, 2024 17:48
Copy link
Contributor

@Veratil Veratil left a comment

Choose a reason for hiding this comment

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

Haven't gone through most of widgets/VectorGraph.cpp but wanted to get these comments in now.

@szeli1
Copy link
Contributor Author

szeli1 commented Apr 20, 2024

The core VectorGraphDataArray class needs to be reviewed.
The usage, construction and destruction of FloatModel-s need to be reviewed.
Function getSamples() (and related functions) needs to be reviewed.
Spelling should be checked.

@szeli1 szeli1 requested a review from Veratil April 29, 2024 17:47
Copy link
Contributor

@Veratil Veratil left a comment

Choose a reason for hiding this comment

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

Still haven't gone over the 4000 lines VectorGraph.cpp yet...

std::vector<VectorGraphPoint> m_dataArray;


// baking
Copy link
Contributor

Choose a reason for hiding this comment

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

Cookies? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got the name from texture baking. I guess what you are trying to say is this name does not fit in...

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, no. I was just leaving some fun commentary for a laugh. It's a big PR and going through a ton of lines can get mind numbing.
If the phase name makes sense, then it's fine to keep.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok no problem. I appreciate your commentary. This is a big pr and even as someone who understands it reviewing all the comments and functions was tiring. Thanks for reviewing this.

@szeli1
Copy link
Contributor Author

szeli1 commented May 4, 2024

Added new class and moved the input dialog and the context menu there.
This does not effect any reviewed code up to this point. (but the new class needs to be reviewed)

@szeli1 szeli1 requested a review from Veratil May 4, 2024 15:10
@szeli1
Copy link
Contributor Author

szeli1 commented May 6, 2024

Added new m_isAnEffector bool and related get and set, removed more than 4 heap allocations. Separated m_effectOnlyPoints to m_effectPoints and m_effectLines allowing better transitions between automated points.

@szeli1
Copy link
Contributor Author

szeli1 commented May 7, 2024

Most of the heap allocations were removed.
In the current configuration:
The best case for PaintEvent is 0 heap allocations.
The worst case for PaintEvent is 1 heap allocation.
The best case for updating the samples (getSamples()) is 0 heap allocations.
A reasonable worst case for updating the samples is 4 heap allocations.

This is after removing 9 heap allocations.

Added new button in WaveShaper for simplifying the graph. This can improve performance.

Copy link
Contributor

@Rossmaxx Rossmaxx left a comment

Choose a reason for hiding this comment

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

The changes are too large. I have reviewed it like i could. Also, consider splitting the file up (or using Qt/STL mechanisms where you can)

#define LMMS_GUI_VECTORGRAPH_H

#include <vector>
#include <array>
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice to arrange headers in alphabetical order within the groups.

Copy link
Contributor

Choose a reason for hiding this comment

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

In all files

@messmerd
Copy link
Member

The controls seemed really unintuitive to me and I couldn't figure them out until I read the instructions in this PR, but even then it was a struggle.

The first thing I did was click and drag in an empty spot of the WaveShaper graph, expecting it to create a new node and move it around (like in the Automation Editor), but it behaved very differently.

There's text at the bottom of the WaveShaper graph showing the options, but it's not very legible and it's awkward to use. It took me a few minutes to realize that despite each option being a simple box, they function like knobs and you have to click and drag up or down to adjust them. I think it would be better to have a proper toolbar and some knobs like other editing windows in LMMS. If the size of the WaveShaper window needs to be increased to accommodate it, that's fine.

I also don't understand how the white waveform works and why it is needed.

I did notice a bug when I went to modify a Y coordinate of a node - I clicked "Cancel" in the input box but it modified the node anyway, setting it to zero. However, I wasn't able to replicate it. When I tried again, there were were two consecutive input boxes that appeared and I'm not sure why or what data it was editing.

Overall I'm impressed with the functionality the new vector graph widget provides, and being able to automate the nodes is awesome. I just think it needs some work to improve the user experience.

@Rossmaxx
Copy link
Contributor

It just randomly crossed my mind. How about using some functions from the automation track instead of implementing everything yourself (i believe there might be atleast a 10% similarity between both)

@szeli1
Copy link
Contributor Author

szeli1 commented Jun 24, 2024

I also don't understand how the white waveform works and why it is needed.

Different waveforms or graphs can interact with each other. In this case the white graph can change the green one.
Currently this can help in automation:
We want to automate multiple points on the green graph slightly differently, instead of making multiple automation clips, we can automate a point on the white graph and in the settings of the green graph, we can enable the option "add", which would add the white graph's values to the green graph.

But in other configurations, an (input) graph might be used to display a signal for example. In that case the output graph can react to the input graph, changing the output values accordingly.

Older example:
Finished9
Here the blue graph is added (between 1. and 2. point) to the red, and then multiplied by the red (after the 2. point)

I think it would be better to have a proper toolbar and some knobs like other editing windows in LMMS

I might add a new window for editing graphs.

@szeli1
Copy link
Contributor Author

szeli1 commented Jun 24, 2024

consider splitting the file up (or using Qt/STL mechanisms where you can

What do you mean by "splitting the file up" and "Qt/STL mechanisms"?

@szeli1
Copy link
Contributor Author

szeli1 commented Jun 24, 2024

It just randomly crossed my mind. How about using some functions from the automation track instead of implementing everything yourself (i believe there might be atleast a 10% similarity between both)

Probably not possible. The 2 systems are really different currently. But I might copy some things from the Automation Clip.

@Rossmaxx
Copy link
Contributor

splitting the file up

The file is too large, might as well split up the functionality to a base file and helper functions in another file. If it ain't possible, leave it as is.

Qt/STL mechanisms

There might be something already possible through Qt/STL functions which you seemed to have missed out on. I haven't looked too close on the logic but that's what i feel from a quick glance. Lemme look again.

@szeli1
Copy link
Contributor Author

szeli1 commented Jun 24, 2024

There might be something already possible through Qt/STL functions which you seemed to have missed out on. I haven't looked too close on the logic but that's what i feel from a quick glance. Lemme look again.

I noticed I've reimplemented some kind of sorted map, but Qt does not provide the necessary functions that I use for optimization. (such as finding the nearest x position of a point)

@szeli1 szeli1 requested a review from Rossmaxx June 30, 2024 14:27
@szeli1
Copy link
Contributor Author

szeli1 commented Aug 1, 2024

I have added bezier curves, reviews are welcome especially for workflow and VectorGraphModel

@bratpeki
Copy link
Member

I'll be testing this. We need the waveshaper updated ASAP.

@bratpeki
Copy link
Member

Before anything, I would like to ask if we could make the waveshaper window bigger? At least 150% of the current size. As of now, it is ridiculously tiny.

Secondly, before a PR like this is made, it needs a cleaner UI.

What is this white line at the bottom? Why is it there?

screencap-2024-08-26-23-36-31

Why is the text not at least a bit padded. Also, the text in the middle is pretty difficult to read.


So, for starters, I would argue this needs a bigger UI, and a bit of a color rework.

Copy link
Contributor

@Rossmaxx Rossmaxx left a comment

Choose a reason for hiding this comment

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

Half a review done. I'll try reviewing the rest soon. Fix the codefactor whitespace checks.

#define LMMS_GUI_VECTORGRAPHMODEL_H

#include <vector>
#include <array>
Copy link
Contributor

Choose a reason for hiding this comment

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

I already said about arranging includes but looks like you forgot it. Within the sections arrange horizontally.

The format is
Header corresponding to .cpp file (if applicable)

Std headers

Qt headers

Lmms headers

// 3 - sineB
// 4 - peak
// 5 - steps
// 6 - random
Copy link
Contributor

Choose a reason for hiding this comment

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

How about using enum instead? Or enum class since dom made that change.

// point changed inside VectorGraphDataArray m_dataArray or m_maxLength changed
void dataChanged();
void updateGraphView(bool shouldUseGetLastSamples);
// signals when a dataArray gets to 0 element size
Copy link
Contributor

Choose a reason for hiding this comment

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

For longer comments, do the doxygen comment.

m_dataArrays[i].setEffectorArrayLocation(-1, false);
}
// setting updated locations
for (unsigned int i = 0; i < m_dataArrays.size(); i++)
Copy link
Contributor

Choose a reason for hiding this comment

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

Size_t here too?

std::vector<FloatModel*>* automationModels = m_dataArrays[i].getAutomationModelArray();
bool isSaveable = m_dataArrays[i].getIsSaveable();
// general saving attributes
me.setAttribute(readLocation + "DataArraySize", isSaveable == true ? static_cast<unsigned int>(m_dataArrays[i].size()) : 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

You can avoid the static cast if you use size_t

void VectorGraphModel::saveSettings(QDomDocument& doc, QDomElement& element, const QString& name)
{
#ifdef VECTORGRAPH_DEBUG_PAINT_EVENT
qDebug("saveSettings start");
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider using std::cerr instead in core. Change other occurences too.

// generating 2 seeds and blending in between them
for (unsigned int i = 0; i < randomValuesSize / 2; i++)
{
m_universalSampleBuffer[i] = std::fmod((static_cast<float>(rand()) / 10000.0f), 2.0f) - 1.0f;
Copy link
Contributor

Choose a reason for hiding this comment

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

For rand, there's a fast_rand() in lmms_math.h. use that.

float blend = 10.0f + randomSeed * 10.0f;
int randomSeedB = static_cast<int>(blend);
blend = blend - randomSeedB;

Copy link
Contributor

Choose a reason for hiding this comment

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

No need to seed if using fast_rand.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seeds are used to create the desired behavior.

Copy link
Contributor

@Rossmaxx Rossmaxx left a comment

Choose a reason for hiding this comment

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

More follow up reviews needed, here's the second

class VectorGraphDataArray;
class FloatModel;

using PointF = std::pair<float, float>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't you have the same line in VectorGraphView.h too. It should trickle down here.

friend class lmms::gui::VectorGraphView;
};

class LMMS_EXPORT VectorGraphDataArray
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice if you split this class into it's own seperate file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would rather not do this, it will make it harder to read

m_wavegraphModel( 0.0f, 1.0f, 200, this ),
m_clipModel( false, this )
m_vectorGraphModel(1024, this, false),
m_clipModel( false, this ),
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
m_clipModel( false, this ),
m_clipModel(false, this),

if (m_vectorGraphModel.getDataArraySize() > 0)
{
int size = 0;
char * dst = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
char * dst = 0;
char* dst = 0;

Copy link
Contributor

Choose a reason for hiding this comment

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

also since this is a pointer, nullptr would be better.

#include <cmath> // sine
#include <cstdlib> // rand
#include <vector>
#include <QMutex> // locking when getSamples
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe there's something with RequestChangesInModel and DoneChangesInModel which should be similar. @sakertooth can explain.

dataChanged();
}

void VectorGraphDataArray::formatArray(std::vector<PointF>* dataArrayOut, bool shouldClamp, bool shouldRescale, bool shouldSort, bool callDataChanged)
Copy link
Contributor

Choose a reason for hiding this comment

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

you can break this function down into sortArray, RescaleArray, clampArray, and call these helpers from within this function, will get rid of codefactor warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The order of these operations can not be changed and the dataChanged() will have to be implemented and future users of these functions would have to understand when to call dataChanged().

unsigned int VectorGraphDataArray::setX(unsigned int pointLocation, float newX)
{
int location = pointLocation;
if (m_isFixedX == false && newX <= 1.0f)
Copy link
Contributor

Choose a reason for hiding this comment

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

would make sense to invert this too.

Suggested change
if (m_isFixedX == false && newX <= 1.0f)
if (m_isFixedX == true || newX > 1.0f)

int targetLocation = location;
// bool dataChangedVal = false;
// if getNearestLocation returned a value
if (location >= 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

another place where inversion might help

}
else
{
location = pointLocation;
Copy link
Contributor

Choose a reason for hiding this comment

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

if you decide on inverting the conditions, you can return pointLocation directly in these 2 areas.

float output = attribValue;

// none
if (getEffect(pointLocation, effectSlot) == 0) { return output; }
Copy link
Contributor

Choose a reason for hiding this comment

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

switch case exists.

@szeli1
Copy link
Contributor Author

szeli1 commented Aug 27, 2024

Secondly, before a PR like this is made, it needs a cleaner UI.
Why is the text not at least a bit padded. Also, the text in the middle is pretty difficult to read.

Few pictures show outdated GUI.

What is this white line at the bottom? Why is it there?

In the screenshot in your comment the white line acts as a clamp for the green line.
I will rewrite the PR description because nobody seems to understand this "white line" feature.

@bratpeki
Copy link
Member

bratpeki commented Sep 3, 2024

I'm testing it now.


Why is this line so broken?

screencap-2024-09-03-17-26-39

Zoomed in:

image


I'm sending a video of me trying the feature out, along with some timestamps and questions. Ignore the Discord notification.

test.mp4

0:02 : The way the buttons at the bottom pop up is very unexpected. The user wants to move the point without some options popping up and changing the curve from its original shape.

0:03 : When the button at the bottom activate, the background doesn't follow them. Example:

screencap-2024-09-03-17-42-53

The buttons also can't be closed with Esc. The curve looks like it has tiny "saw teeth", instead of being completely straight.

0:11 : You said this PR is done, right? Why do the buttons stick so close to the left? Why are they lowercase? Why does the Simplify" button have different capitalization depending on the letter? Why is its button so small? It can't fit all those letters. I think "switch graph" doesn't explain much. What is that other graph? Why use it?

0:13 : So, clicking to create a new point removes the buttons from the bottom, and clicking to change the button position opens it again. If I just wanted to move a point, I don't think I'd want that to pop up. It seems like an "advanced functionality". I also feel like that should be closeable, so I can return to editing points.

0:19 : The point placement is very nice. I don't know how, but I got one of the pins moving the wrong direction relative to the mouse! 🤣

0:24 : Also, while moving pins, the curve "twitches".

0:48 : Also twitches for that white curve thing.

1:00 : This really can't be called a finished PR with this UI.

1:21 : I tried giving the point a value. I'm expecting that the x value is cemented, and you edit the Y value by double-clicking, much like the lollipops in the MIDI editor, or the automation editor point. However it opens two inputs? Are those for X and Y?


Not regarding the video, this doesn't look quite right, but I may be wrong with this and maybe it should really be like this:

screencap-2024-09-03-17-46-11
screencap-2024-09-03-17-46-18

Also, why is there just a random 0 in the top-left??? 😂


I'm waiting on responses, but this now seems like either a very complex plugin, or a plugin that still demands a fair bit of work. As for the vector graph implementation, that's a bit too technical for me, so I can't comment on it.

@szeli1
Copy link
Contributor Author

szeli1 commented Sep 3, 2024

Why is this line so broken?

It is probably because 200 samples are mapped to the 206 sample display. This is an optimization the graph uses to save recomputing the sample points.

0:02 : The way the buttons at the bottom pop up is very unexpected. The user wants to move the point without some options popping up and changing the curve from its original shape.

How would you like the options to show up? The user can use the editing window if they don't want to see the options.

0:03 : When the button at the bottom activate, the background doesn't follow them. Example:

Yes. I might add an option to scale the background to the correct size when the options show up, but I don't know if it is necessary.

The buttons also can't be closed with Esc.

I will change how the shortcuts work in a future PR. I think this is an unnecessary shortcut since the user can press the right mouse button to end the point selection.

0:11 : You said this PR is done, right? Why do the buttons stick so close to the left? Why are they lowercase? Why does the Simplify" button have different capitalization depending on the letter? Why is its button so small? It can't fit all those letters.

I think "switch graph" doesn't explain much.

It changes which graph is edited. How would you like to communicate this better?

What is that other graph?

Explained in PR body and in other comments.

Why use it?

Explained in PR body and in other comments.

0:13 : So, clicking to create a new point removes the buttons from the bottom, and clicking to change the button position opens it again. If I just wanted to move a point, I don't think I'd want that to pop up. It seems like an "advanced functionality". I also feel like that should be closeable, so I can return to editing points.

How to open that window without covering the graph in some way while not hiding the options?
I think the scaling issue is not that big. I will make it so when the points are moved, the options menu will close.

0:19 : The point placement is very nice. I don't know how, but I got one of the pins moving the wrong direction relative to the mouse! 🤣

I will look into that.

0:24 : Also, while moving pins, the curve "twitches".
0:48 : Also twitches for that white curve thing.

This is again because of the sample difference. Editing will cause it to draw 206 samples while the plugin uses 200 samples, so when the plugin updates the displayed graph will change. I don't consider this a big issue.

1:00 : This really can't be called a finished PR with this UI.

I think the UI is 95% finished.

1:21 : I tried giving the point a value. I'm expecting that the x value is cemented, and you edit the Y value by double-clicking, much like the lollipops in the MIDI editor, or the automation editor point. However it opens two inputs? Are those for X and Y?

Yes, it makes sense that you think that. I could change that if you want. At least the input dialogs should display which value you are editing.

Not regarding the video, this doesn't look quite right, but I may be wrong with this and maybe it should really be like this:

I don't know what are you trying to show. The curve is mirrored but it displays correctly.

Also, why is there just a random 0 in the top-left??? 😂

Explained in PR body and in other comments.
It displays which graph has focus currently.

I'm waiting on responses, but this now seems like either a very complex plugin

I would like to mention that I think you didn't really test the features this graph offers. Keep in mind that this graph isn't for this plugin only, it is like the existing Graph Your comment might be true because I didn't write a good enough description for it, but In my view calling it complex doesn't mean a lot when you don't know how it works. An inexperienced user might call LMMS complex and unnecessary while an experienced user knows how things work and why they work like that. Being complex isn't a drawback when the basic graph has a learning curve of 20 seconds. The user doesn't have to use the complex features, but if those features are missing, then all users will be limited.

An information button would get rid of the "complex" argument tho.

@regulus79
Copy link
Contributor

The options which appear at the bottom of the vector graph--would it look better if they were in a right-click context menu or something? Currently to me the way they don't have a background and take up a sizable portion of the graph doesn't seem quite right, idk.

(disclaimer, I haven't actually tested the pr yet. I should really do that, sorry)

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.

6 participants