Drawing charts


#1

Hello,
could anyone help me? How to make graph of math formula? I mean something like drawing synthesiser envelope shape? I have also sliders that control independently attack, decay, sustain and release. And I would like the envelope graph (chart) will be updating with my sliders changing.
I tried to looking for something like that, but all tutorials and people on forums tell only about juce::paint and graphics, which actually allow me to draw already defined shapes like lines, rectangles, triangles and things like that.
I wonder, maybe I should just create my own class and inherit from graphics, and then create my own function that would draw from math formula?
I am not sure what to do. Sorry if it’s stupid question but I am quite new in JUCE and in C++, and even in programming at all, and it’s difficult for me :slight_smile:


#2

Of course I know I can draw simply envelope graph with simple lines. But my envelope is built from logarithmic lines, and I even have slider that changes those curves and I want to see those changes. That is why I need something to draw math formula.


#3

Inherit from Component and implement the paint-method to draw your formula. You may need to resort to drawing your curve with individual drawLine calls on the Graphics object provided to the paint-method unless there’s some method in Graphics that replicates the shape of your math formula.


#4

Hello,
thanks for your reply. But I am sceptic about component paint method.
As I remember, even when I drawn something really simple with that method and made it resizeable according to windows resize there was always some lagging.
So I think component paint method is good for some static graphics.
But maybe I am wrong, please correct me if I am.

I am also looking farther in the future, and I wonder how to let that graph of envelope to be click-drag able, so I could set ADSR parameters by dragging graph lines end points, or even to manipulate curves by dragging the whole lines.
And then to make a sense, sustain point should be able to draging in 2D (left/right to set decay, and up/down to set sustain level)

Oh man… :slight_smile: Maybe I am little impatient. But there is a lot of things to discover in JUCE, and discovering goes very slow and hard for me as begginer.


#5

Yeah, you’re misinterpreting whatever it was that you saw there.

For 2D graphics, paint() is pretty much optimal. Of course, it can go really slowly if you do something dumb in there. But it’s pretty much the same performance you’ll see from any other toolkit/platform, because they all do basically the same thing. For example on OSX or iOS, drawing stuff in paint() just calls straight through to CoreGraphics, just like a native OSX app does.


#6

OK, so I need to make more experiences.


#7

OK, thanks for reply.
So maybe other question. Is it good idea to draw chart by drawing several small circles (for example 2 pixels diameter), and give them coordinates relative to my f(x) math formula?


#8

No again :wink:
Best is to create a Path and use startNewSubPath() and lineTo(). This gives you a good start.

Good luck


#9

If you want several small circles, then yes, drawing several small circles is probably a good idea…


#10

Oh man, but Daniel above your post wrote it’s not good (can you see his post?)
He told to use startNewSubPath() and lineTo().
But I’ve just read on startNewSubPath() and lineTo() and as I understand it draws line from point to point, so it would be straight line, not curve. Maybe Daniel thought about drawing several very short straight lines to provide curve. But then it’s much less optimal (at least for my brain) than drawing just several circles - where each one has just two coordinates (for line you need two coordinates for both ends, so you need 4 coordinates).
So I have realy mess in mind.


#11

Ok sorry, I don’t know where you are with your skills. I meant to get you going. Any paint call will boil down to a lot of lines rasterised to pixels by the OS.
I mean to be fair, even better than to paint circles, which all have round shapes that need antialiasing and so forth…

You will get good results, if each line segment is about a pixel.

Another method is to use cubicTo(), if you know how to compute the parameters from your formula. But don’t be disappointed if you care to look at the internals, the PathFlateningIterator will slice down your path and create a lot line segments…

But you say it right, start experimenting and find your way of doing it.

Good luck


#12

OK, thanks I will try to experimenting.
Best Regards


#13

Hello Boys and Girls.
I am so happy, I’ve just drawn my envelope shape. I finally used Daniel advice:
Path and use startNewSubPath() and lineTo()
Great thanks for your help. But now I want more, more more… :slight_smile:
How to update my envelope graph?
I have knob that allow me (by AudioProcessorValueTreeState) to change parameter of my logarithmic formula, so I can change shape of curves of Attack, Decay etc.
And it works for audio. As a beginner I am really excited. But unfortunately it doesn’t work for graph. Graph is static with parameters which are initialised on the beginning.
I paint it in the JUCE::AudioProcessorEditor, which is main parent class for all other classes, and it is invoked only one time on the beginning. I can’t execute AudioProcessorEditor more than one, or I don’t know how.

So I tried inherit JUCE::Component class in my JUCE::AudioProcessor, where I have my AudioProcessorValueTreeState, audio is updating all the time, and where all things happen. So there I overrided Component::paint(Graphics& p), and resized() where I added bounds for my envelope, and than addAndMakeVissible in constructor, but it doesn’t paint anything. xCode doesn’t show me any errors, but it just doesn’t paint.
Could anyone give me any advice how to make it work?
Great thanks in advance and best regards.


#14

Add a listener to whatever is changing the settings and add the appropriate callback for this (for example, inherit from Slider::Listener, do mySlider.addListener (this) and add void sliderValueChanged (Slider* slider) to your editor if using a slider to change the curve settings) and call repaint inside of here. This will trigger the paint function to be called again every time you move the slider you have attached the listener to

edit: a bit of clarification


#15

Ok, great thanks, but what exactly do you mean by …repaint…
Do you mean to call addAndMakeCisible, or resized(), or paint(Graphics& p)?
I am asking because paint require something as a “Graphics& p”. I don’t know what to put there. I see paint function is nowhere called in my code, it happesn somewhere deep in JUCE framework. So I am not sure how to recall that function.


#16

That is painfully wrong, don’t do that.


#17

OK OK OK, I was just experimenting. I am still beginner. :slight_smile:


#18

Thanks for your warning, but I would be more grateful if you tell why it’s wrong, beside that it doesn’t work. :slight_smile:


#19

There is a function in there component class called repaint()which will mark the entire component as needing to be drawn again. This is useful when you want to update the look of a component (like you do here). Just write repaint() inside the slider changed callback :slight_smile:


#20

Hey man I fill I am closer to the solution, thanks. I made it, and it works, but the problem are:

  1. there are great lagging, knob and graph work not fluid, just jumping and lagging
  2. When I rotate the knob it’s repainting but it multiplies the curves, you know what I mean? all changes are visible on the screen, changes doesn’t clear the screen, so when I rotate knob I have a lot of curves. It should clear the screen each time I rotate knob, and show only one curve, updated one to appropriate value of my knob.

Maybe you could help if I show you my code for sliderValueChanged:

void WojtekSynthAudioProcessorEditor::sliderValueChanged (Slider slider)
{
if(slider == &attackShape)
{
for (int i=0; i<=1000; i++)
{
envGraphWindow.myPath.lineTo(envGraphWindow.startPointX+(i/10),
envGraphWindow.startPointY - (100
envGraphWindow.adsrGraph.wLogShape((float)i/1000.0f)));
}
envGraphWindow.adsrGraph.wLogShapeManip(slider->getValue());
envGraphWindow.repaint();
addAndMakeVisible(&envGraphWindow);

    }
}