-
-
Notifications
You must be signed in to change notification settings - Fork 1k
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
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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.
The core |
There was a problem hiding this 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...
include/VectorGraph.h
Outdated
std::vector<VectorGraphPoint> m_dataArray; | ||
|
||
|
||
// baking |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cookies? :)
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Added new class and moved the input dialog and the context menu there. |
Added new |
Most of the heap allocations were removed. This is after removing 9 heap allocations. Added new button in WaveShaper for simplifying the graph. This can improve performance. |
There was a problem hiding this 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)
include/VectorGraph.h
Outdated
#define LMMS_GUI_VECTORGRAPH_H | ||
|
||
#include <vector> | ||
#include <array> |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In all files
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. |
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) |
What do you mean by "splitting the file up" and "Qt/STL mechanisms"? |
Probably not possible. The 2 systems are really different currently. But I might copy some things from the Automation Clip. |
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.
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) |
I have added bezier curves, reviews are welcome especially for workflow and |
I'll be testing this. We need the waveshaper updated ASAP. |
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? 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. |
There was a problem hiding this 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> |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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++) |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; | ||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this 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>; |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m_clipModel( false, this ), | |
m_clipModel(false, this), |
if (m_vectorGraphModel.getDataArraySize() > 0) | ||
{ | ||
int size = 0; | ||
char * dst = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
char * dst = 0; | |
char* dst = 0; |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
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) |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
switch case exists.
Few pictures show outdated GUI.
In the screenshot in your comment the white line acts as a clamp for the green line. |
I'm testing it now. Why is this line so broken? Zoomed in: I'm sending a video of me trying the feature out, along with some timestamps and questions. Ignore the Discord notification. test.mp40: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: The buttons also can't be closed with 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: 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. |
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.
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.
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.
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.
It changes which graph is edited. How would you like to communicate this better?
Explained in PR body and in other comments.
Explained in PR body and in other comments.
How to open that window without covering the graph in some way while not hiding the options?
I will look into that.
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.
I think the UI is 95% finished.
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.
I don't know what are you trying to show. The curve is mirrored but it displays correctly.
Explained in PR body and in other comments.
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 An information button would get rid of the "complex" argument tho. |
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) |
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 inpaintEvent
. 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 editedVectorGraphDataArray
.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
VectorGraphDataArray
s.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 callgetValues
(renamed togetSamples
)). need testingcall
dataChanged()
insetDataArray
journalling in
PointGraphModel
setDataArray
keep attributes option,formatArray
option which runsformatArray
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()
inputsepareate
m_isAutomatableEffectable
deallocate
m_universalSampleBuffer
?use ComboBox::DEFAULT_HEIGHT
add comments in VectorGraphViewBase
implement Model::dataChanged
filter implementation
remove qDebug
Final WaveShaper VectorGraph implementation:





Different
VectorGraphDataArrays
effecting each other, one is automated: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