How do I push a class instance in to a vector and make it visible

Hi there

Im relatively new to JUCE and c++, so go easy on me!

I have a Spheres class that draws a circle on my plug-ins UI. instead of adding a class instance one at a time, I would like to push multiple instances into a vector and make them visible.

Heres how I’m going about it so far. Hopefully someone will be able to direct me in the right direction.

Im putting my vector in private section of my pluginEditor.h file

std::vector<Spheres> sphereArray;

…and in the pluginAudioProcessorEditor constructor of my pluginEditor.cpp file I’m putting

for (int i = 0; i < 4; i++){
        sphereArray.push_back(new Spheres());
    }

The error I’m getting is “No matching member function for call to 'push_back” can anyone point out where I’m going wrong?

And once these instances are successfully pushed back in to my vector how would I make them all visible?

new Spheres() will return a Spheres* (pointer-to-Spheres) but your vector holds Spheres instances directly.

You could try something like this:

std::vector<std::unique_ptr<Spheres>> sphereArray;

// later...
for (auto i = 0; i < 4; ++i)
{
    sphereArray.push_back (std::make_unique<Spheres>());
    addAndMakeVisible (*sphereArray.back());
}

Hey, thanks for the reply!

hmm,

addAndMakeVisible(sphereArray.back());

gives an error reading “No matching member function for call to 'addAndMakeVisible”

that’s because addAndMakeVisible expects the underlying object. use *sphereArray.back()

edit:
if you want to have dynamic components vector<unique<component>> is good already, but having dynamic components can yield a lot of problems, like when you just remove one while it is in the middle of performing some action. you might want to consider instead use std::array<juce::Component, numComponents> with numComponents being some static constexpr int, so that it can be made on compile time. this is a good solution if you want to have the benefits of arrays, like indexing with enums and stuff, but don’t really need to have more than a specific amount of them. then you could just setVisible the ones that are currently supposed to be used

Sorry, that’s what I get for posting code without compiling it. Adding a * before sphereArray.back() should fix it.

Thanks @Mrugalla & @reuk

Well its certainly compiling now! Im just stuck on actually rending my class instances to the UI

Before I was animating a class instance (sphere) by calling sphere.repaint() in the timerCallback function, I was using the output from a couple of sliders to move the x & positions of the circle, before calling sphere.setBounds(getLocalBounds()) in resize, this would successfully render the lone animated circle to the UI. How would I typically go about that now I have a vector of circles? (Im using a cool bit a math that will move each circle around independently, based of the position of the first circle. So the circles won’t be drawing over each other)

Instinctually coming from over languages I would do the same as above, only cycle through the class instances in a for loop I.e.

//inside timerCallback()
for (int i = 0; i < sphereArray.size(); i++ ) {
    sphereArray[I].repaint();
}
...
//resize
for (int i = 0; i < sphereArray.size(); i++ ) {
    sphereArray[i].setBounds(getLocalBounds());
}

This isn’t working and Im no doubt a million miles off the correct solution. Alas how would you go about then rendering the other class instances to the screen?

Thanks for the help so far, I really appreciate it!

I’m assuming sphereArray is a vector of std::unique_ptr of Spheres

//inside timerCallback()
for (auto& sphere : sphereArray ) 
{
    sphere->repaint();
}
...
//resize
for (auto& sphere : sphereArray) 
{
    sphere->setBounds (getLocalBounds());
}

Hey, it is yes.

Im most baffled, Im not getting any class instances rendered to the UI. Dose any one know any common mistakes typically made at this juncture?

Thanks again!

It is hard to guess without looking your MainComponent code.

Does drawing a single instance of Sphere (without vector) work?

typically all that is required to let a component appear is to addAndMakeVisible(comp) in the constructor and comp.setBounds() in resized(). sometimes components don’t seem to appear because the calculations for the bounds are wrong and actually place it outside of its parents bounds. a component’s setBounds method always wants numbers that are relative to its parent instead of the whole window

Maybe someone can spot something I can’t. When I declare a lone instance ,call repaint and and call classinstance.setBounds(getLocalBounds()) in resize is appears fine. This is not the case now Im reading from a vector of class instances.

Heres my code, with the Spheres and pluginEditor .h / .cpp

Spheres. h

class Spheres  : public juce::Component
{
public:
    Spheres(); 
    ~Spheres() override;
    
    void setPosition(float x_, float y_);
    void paint (juce::Graphics&) override;
    void resized() override;

private:
    
    float x;
    float y;
    int sphereRadius { 10 };
  
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Spheres)
};

Spheres.cpp

Spheres::Spheres() 
{
    x = 0;
    y = 0;
}

Spheres::~Spheres()
{
}

void Spheres::setPosition(float x_, float y_)
{
    x = x_;
    y = y_;
}

void Spheres::paint (juce::Graphics& g)
{
    //the spheres should be drawing to the middle of the screen.
    g.setOrigin(getWidth() / 2 - 10, getHeight() / 2 - 10);
    g.setColour(juce::Colours::red);
    g.drawEllipse(x, -y, sphereRadius * 2, sphereRadius * 2, 2);
}

void Spheres::resized()
{
    
}

pluginEditor.h

class Mandelbrot_pluginAudioProcessorEditor  : public juce::AudioProcessorEditor
                                              ,public juce::Timer
                                              ,public juce::Slider::Listener
{
public:
    Mandelbrot_pluginAudioProcessorEditor (Mandelbrot_pluginAudioProcessor&);
    ~Mandelbrot_pluginAudioProcessorEditor() override;
    int borderRadius = 180;
    float t { 0.0 };  //==============================================================================
    void paint (juce::Graphics&) override;
    void resized() override;
    void timerCallback() override;
    void sliderValueChanged(juce::Slider* slider) override;

private:
    
    juce::Slider xPos_Slider;
    juce::Slider yPos_Slider;
    std::vector<std::unique_ptr<Spheres>> sphereArray;
    
    // This reference is provided as a quick way for your editor to
    // access the processor object that created it.
    Mandelbrot_pluginAudioProcessor& audioProcessor;
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Mandelbrot_pluginAudioProcessorEditor)
};

pluginEditor.cpp

Mandelbrot_pluginAudioProcessorEditor::Mandelbrot_pluginAudioProcessorEditor (Mandelbrot_pluginAudioProcessor& p)
    : AudioProcessorEditor (&p), audioProcessor (p)
{
    setSize (600, 400);
    
    for (auto i = 0; i < 4; i++)
    {
        sphereArray.push_back(std::make_unique<Spheres>());
        addAndMakeVisible(*sphereArray.back());
    }
        
    //add sliders, set range, value ect..
    xPos_Slider.setSliderStyle(juce::Slider::SliderStyle::LinearVertical);
    xPos_Slider.setRange(-1.0f, 1.0f, 0.01f);
    xPos_Slider.setValue(0.0f);
    xPos_Slider.addListener(this);
    addAndMakeVisible(xPos_Slider);
    
    //ypos slider
    yPos_Slider.setSliderStyle(juce::Slider::SliderStyle::LinearVertical);
    yPos_Slider.setRange(-1.0f, 1.0f, 0.001f);
    yPos_Slider.setValue(0.0f);
    yPos_Slider.addListener(this);
    addAndMakeVisible(yPos_Slider);
     
    // start timer to create a loop for animation
    Timer::startTimer(60);
}

Mandelbrot_pluginAudioProcessorEditor::~Mandelbrot_pluginAudioProcessorEditor()
{
    Timer::stopTimer();
}

//==============================================================================
void Mandelbrot_pluginAudioProcessorEditor::paint (juce::Graphics& g)
{
    // drawing big circle with rotating arm
    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
    g.setOrigin(getWidth() / 2, getHeight() / 2);
    g.drawEllipse(0 - borderRadius, 0 - borderRadius, borderRadius * 2, borderRadius * 2, 2);
    g.drawLine(0, 0, borderRadius * cos(t), borderRadius * sin(t), 2);
}

void Mandelbrot_pluginAudioProcessorEditor::timerCallback()
{
    repaint();
   
    for (auto& sphere : sphereArray )
    {
        sphere->repaint();
        sphere->setPosition(xPos_Slider.getValue(), yPos_Slider.getValue());
    }
    
    t += 0.01;
}

void Mandelbrot_pluginAudioProcessorEditor::sliderValueChanged(juce::Slider *slider)
{
    if (slider == & xPos_Slider)
    {
    }
    else if (slider == & yPos_Slider)
    {
    }
}

void Mandelbrot_pluginAudioProcessorEditor::resized()
{
    // set positions
    for (auto& sphere : sphereArray)
    {
        sphere->setBounds (getLocalBounds());
    }

    xPos_Slider.setBounds(getWidth() / 12, getHeight() / 2 - 100, 20, 200);
    yPos_Slider.setBounds(getWidth() / 12 + 50, getHeight() / 2 - 100, 20, 200);
}

Im wondering if anyone can spot a reason why the four Spheres class instances are not being rendered?

Thanks for your help so far!

ok let’s see.

first of all i’d rename Spheres to Sphere, because apparently it represents a single sphere, right?

then i’d get rid of the setPosition method and instead use the method of its base class juce::Component::setCentrePosition(int x, int y) for this. then you don’t have to keep members for x and y anymore as well.

then i would put into resized:

if(getWidth() != sphereRadius || getHeight() != sphereRadius)
{
    setSize(sphereRadius, sphereRadius);
    return;
}

that would make your component always have a width and height of your desired radius no matter what its parent component tries to give it as bounds. (if you don’t plan to ever change their radius you can even call their setBounds()-method from the constructor and only setCentrePosition in their parent’s resized for simplicity. but not sure if that is recommendable because it wouldn’t work well with resizability.

in the paint method you only need

g.drawEllipse(getLocalBounds().toFloat(), 1.f);

that draws an ellipse around the current bounds.

in the plugineditor you could then have you vector of spheres (sphereArray). you correctly addAndMakeVisible your spheres after pushing back new instances of them into the vector.
idk what your pluginEditor’s paint method has to do with the spheres but the spheres will be painted on top of this, because they are the children of this component. you can imagine that to be similiar to layers in photoshop. here it’s called the z-order and it’s typically defined by the sequence in which you added components to their parents.

now in your resized method you give every sphere the local bounds as bounds. that will make them all be drawn on top of each other. if you want them to actually arrange themselves in some other way this is the place where you have to define that.

Hey, Ive tried hacking around, still to no avail.

My paint method in my plugin editor has nothing to do the spheres class, the paint method is actually drawing a bigger circle to house the smaller spheres class instances (see picture)

So If I understand correctly I don’t need to call repaint on the class instances because its a child to the pluginEditor? calling repaint on its own will suffice.

The screenshot posted shows a lone class instance appearing exactly where it should, having set its origin to be in the middle of the UI (in the sphere class repaint method). When I move both the sliders its moves the class instance(smaller red circle) around the bigger circle. I suppose what I’m struggling to understand is, if I have correctly addAndMakeVisible my array of spheres why are they not being drawn to the UI like the Lone instance? surely they would behave the same.

Screen Shot 2022-02-07 at 11.58.45

Apologies is this is dragging on!
Thanks for your help.

every component that has been added to a component is being repainted correctly if that component isShowing(), which is like isVisible() with extra steps. in resized you define all child components’ bounds. the point 0,0 is always the topleft corner of the component you’re in

Hey, Yeah isShowing() is returning true on the vector of class instances. I just can’t for the life of me figure out where they are being drawn. To try and remove some error, I removed both times I had set the origin to the middle of the UI. If I’m drawing my sphere class instance as say 100, 100 in the paint method of my sphere class and I call sphere.setbounds(getLocalBounds()) in the pluginEditor resize method. It should still appear at 100, 100 right?

As before the lone instance will appear as expected, where as the vector of instances is not drawn at all.

put g.fillAll(juce::Colours::red); into your sphere paint so you can be sure that it will always just make a big fat rectangle covering the whole component if it draws at all. that way you can rule out that your paint code is wrong. if you still don’t see the components then you just gave them the wrong bounds. make sure that the numbers you give it for x and y are lower than getWidth() and getHeight() of its parent component. also make sure that they also all have a positive width and height big enough to be seen and that they differ from each other so they won’t overlap. just use absolute numbers as a starting point and work yourself away from there in slow steps to make it responsive

That is good advice, hacking unmissable paint code in for debugging.
Another thing is adding a breakpoint in resized() and step through manually if you are setting reasonable values for every child component when you call setBounds().

Calling setBounds() in resized is preferred over setting it only once in the constructor, that way you can react to size changes later on.

Another caveat: If you call setSize in your main component before creating the child components, because unless the window is resizable, the resized() will only happen once so the children that were created afterwards never get a size.
The solution is to move the setSize() call to the end of the constructor.

Thanks! The way to got them drawn in the end was to call resize() after adding the class instances and making them visible. I just swapped this out with setSize() at the end of the constructor and everything renders as it should. The class instances were not outside the bounds of the parent component, just not visible!

Thanks for all your help, Ive learnt a lot!