Using a 2d array to draw specific pixels

I’m coming from processing, so I’m new to most of the syntax. I’ve got a couple of questions for a project I’m working on:

First, what’s the syntax for making a 2d array with float values? E.g, in processing, the syntax is float[][] var_name = new float[width][height].

Second, how do I manipulate specific pixels on the screen? Preferably by not making 1x1 rects. Again, in processing, there’s the pixel array, and you can just write pixels[index] = color(r, g, b).

Ultimately, I’m trying to have a 2d array that constantly updates the screen by drawing to specific pixels, which are colored to the values of the specific point in the map. So, the pixel for 300,300 would have the value of the 2d array at [300][300].

Any help is appreciated greatly. Thanks!

Hey @KingstownBar , I’m glad you’re giving oF a try! C++ can be a bit challenging to learn so try to hang in there. Unlike Java, there isn’t any garbage collection, so the language has more focus on managing memory and types.

You can create a 2D array of floats on the stack with float myArray[][]. C++ has containers, like std::vector<>, that can be used in place of an array. Good places to learn more about oF and C++ is ofBook, or by studying the projects in the /examples folder, or by searching the forum for related threads.

oF has an ofPixels object. This is a cpu-side representation of an image. It also has an ofImage, which combines an ofPixels and an ofTexture (the gpu-side of the image). Both ofPixels and ofImage have a .setColor() method where you set the color of an individual pixel:

// add this to ofApp.h in an empty project

ofImage image;

// in ofApp.cpp:

void ofApp::setup(){
    // allocate some memory for an image
    image.allocate(300, 300, OF_IMAGE_COLOR); // RGB
    // loop thru and set a random color for each pixel
    for(int y = 0; y < image.getHeight(); ++y){
        for(int x = 0; x < image.getWidth(); ++x){
            float r = ofRandom(255.f);
            float g = ofRandom(255.f);
            float b = ofRandom(255.f);
            ofColor color(r, g, b);
            image.setColor(x, y, color);
        }
    }
    // update the ofTexture in image with its ofPixels
    image.update();
}

void ofApp::draw(){
    image.draw(0.f, 0.f);
}

There are faster ways to set each pixel, say by iterating the ofPixels array in a single loop and then setting an ofTexture from them. But the .setColor() method is a good place to start.

1 Like

This is perfect! Thanks a bunch.

Hey @TimChi , I’ve been running into a problem and was hoping you could help me out again. When I declare a 2d array in the .hpp file of a class, I can’t size it to ofGetWidth() and ofGetHeight(). So, the .hpp file for my Map class looks like this:

#include <stdio.h>

#ifndef _MAP
#define _MAP
#include "ofMain.h" 

class Map {

    public:

  
    void randomize();
    void render();


    ofImage map;
    float grid[ofGetWidth()][ofGetHeight()];
    
    Map();
    
    private:
};
#endif

I get an error whenever if use OfGetWidth() and height stating fields must have a constant size ‘variable length array in structure’, but if I set them to integers there’s no problem. Do you know any solutions to this, other than manually setting the [][] numbers to the screen dimensions?

Hey when class Map gets compiled, it doesn’t know the value returned from ofGetWidth() because the application hasn’t run yet. Because c-style arrays are created with fixed numbers of elements, I think using explicit ints might be the only way to do this. The number of elements in a std::vector can be changed, so you could use those containers. Another way is to create float grid[][] on the heap (dynamic memory) after the application has started, and then access it with a pointer. But I don’t use c-style arrays that often and so I’m not entirely sure about the correct way to do this.

The std::vector is a contiguous block of memory, like an array. The block can move around in memory though, so you have to be careful if accessing it thru a pointer. Calling std::vector::size() returns the number of elements; calling std::vector::data() returns a (current) pointer to the underlying array.

If you want to try with vectors (in pseudo code):

// a std::vector that holds vectors of floats
std::vector<std::vector<float>> grid;

grid.resize(ofGetWidth()); // grid now has ofGetWidth() std::vectors of size 0
for(int i = 0; i < grid.size(); ++i){
    // resize each vector in grid to ofGetHeight() elements
    grid.at(i).resize(ofGetHeight());
}

Another way, populating with random values, 1 at a time:

std::vector<std::vector<float>> grid;

for(int j = 0; j < ofGetWidth(); ++j){
    std::vector<float> temp;
    for(int i = 0; i < ofGetHeight(); ++i){
        float value = ofRandom(1.0);
        temp.push_back(value);
    }
    grid.push_back(temp);
}

Another way:

std::vector<std::vector<float>> grid;

grid.resize(ofGetWidth());
for(size_t i{0}; i < grid.size(); ++i){
    grid.at(i) = std::vector<float>(ofGetHeight());
}

@TimChi thanks for your response! I’ve got one last (hopefully) question. I’ve created two classes, a Map and an Agent class. The map class stores a 2d array of values and renders them using the ofImage solution you suggested. I’m having difficulty, however, making the agents be able to modify the map I declare and use in ofApp.

//in ofApp.h

Map map;

That map object is then initialized, updated, and rendered in ofApp.cpp. The map class has a function (setVal) that changes the value of the inputted xy locations on the grid. I can’t figure out how to make my agents (a separate class also declared in ofApp.h) be able to use the setVal function on the map I declared in ofApp.h specifically. So, somewhere, the agents would say something like:

map.setVal(position.x, position.y, MYVAL);

I’ve tried a bunch of things, including using extern, including ofApp.h in my agents.hpp file, etc…, but haven’t been able to figure it out.

Hey yes this can be a bit tricky, mostly because there are different ways to set up the relationships amongst ofApp, Map, and Agent. Its common though for Map and Agent to be separate things, each with an instance in ofApp.

// in ofApp.h
Map map;
Agent agent;

Then, in the Agent class, you could have a public function that accepts a Map instance as an argument, which then calls a public function in Map.

// in ofApp.cpp
// send map to agent
agent.setValuesInMap(map, position.x, position.y);

// in class Agent
// map is maybe big, so pass by reference, not by copy
void Agent::setValuesInMap(Map& map, float x, float y){
    // do something with x and y in instance map
    map.doSomething(x, y);
}

// in class Map
void Map::doSomething(float x, float y){
}

The Map& argument type means that you’re sending the real, official instance map to the function in Agent where it can be modified. This is known as passing by reference. Its nice and fast to do this for large objects where making a copy would require resources. I like to think of a reference as a pointer that you don’t have to dereference to use.

If the classes are all in separate .h files, make sure to #include them as needed, so the complier can find them:

// in ofApp.h
#include "map.h";
#include "agent.h";

// in agent.h
// Agent needs to know what a Map is, so
#include "map.h";

Now, there are lots of other ways to do this. How its done depends a lot on how you want to define the relationships amongst the 3 classes. Some will be more safe or straight forward than others. If this is confusing just post back and I’ll write a quick example that compiles.

Worked great. Thanks for all your help.

1 Like