Interpolate objects along (random) curved path

Hello,

Me again )

Another basic question, which seems no so basic (or maybe I’m just too tired).

I need to float some sprites across the screen along randomly generated curved paths.

So I know how to generate the path (path.moveTo() and perform path.curveTo()

Now I have a vector of ofPoints.

But how do I take an object and interpolate it along this path ?

(or create a random movement torwards a goal…?)

Many thanks
M

I will answer myself: ofBezierPoint

Hey you’ve worked very hard on this project of yours!

I like to use an ofPolyline for this. I use the .curveTo() method with some random “seed” points. There are a few ways to interpolate. I usually use ofPolyline::getPointAtIndexInterpolated() with an incremented float for the index. And I close them so that they’re continuous.

The .curveTo() method also takes an int curveResolution as its 2nd arg, so you can really generate lots of points if you need to. The default value is 20.

ofPolyline has some nuances. The first and last points are handles for the catmull curve, so there is some overlap needed amongst the first and last couple of points to get the curves to close smoothly. If this is confusing just post back and I’ll dig up some code for it.

Also, if you need to get a (const) reference to something in it, make a const reference of the ofPolyline first. This keeps it from updating itself even though nothing in it has changed, which can slow stuff down.

1 Like

Yes, I did spent last couple months working on this project. But soon it will be finished - exhibition on 28th of Jan!
Last touches :slight_smile:

Okkkk! so it’s ofPolyline. Damn! I spent hours chatting with openAI and he talked about all those getPointAtIndex functions, but insisted they are part of ofPath. They are not and I didn’t think of looking in ofPolyline.

Well what I need is actually quite simple thing. Just particles wandering on the screen following random curved paths. I’m not even sure I need to calculate the path for them and set curves Pretty sure there is some magic math formula that can create this pseudo-random movement in real-time (sine/cos…). But I don’t know how… any ideas ?

Thanks for your answer!
Saved me again )
M

I think you can get an ofPolyline out of ofPath if you need one. ofPath will also draw a filled shape, where ofPolyline will not, if that makes any difference.

There are some different ways to use noise with a moving particle. ofNoise might be an option, which is simplex noise, and tends to have a nice, organic result. But it can be expensive to calculate.

There is a random-walker approach, where each particle determines its next step (direction, magnitude) in a random way. This motion can be somewhat jerky and confined, depending. One option would be to narrow the range of directions it can head in, which would smooth it out and probably give it a wider path.

There are also ways to generate “fractal” curves (an ofPolyline), where a particle would head in a general direction, but have some noise as it goes from A to B.

Finally, generating an ofPolyline from .curveTo() with random points will move it more or less smoothly in a defined path. A quick example (that will compile):
ofApp.h:

#pragma once
#include "ofMain.h"
class ofApp : public ofBaseApp{
	public:
		void setup();
		void update();
		void draw();
		void keyPressed(int key);
    void calcPline();
    void drawPline();
    
    ofPolyline pline;
    ofEasyCam easyCam;
};

ofApp.cpp:

#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
    calcPline();
}
//--------------------------------------------------------------
void ofApp::update(){
}
//--------------------------------------------------------------
void ofApp::draw(){
    easyCam.begin();
    drawPline();
    easyCam.end();
}
//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    if(key == 'p'){
        calcPline();
    }
}
//--------------------------------------------------------------
void ofApp::calcPline(){
    // clear the pline
    pline.clear();
    
    // 11 random points
    std::vector<glm::vec3> seeds(11);
    for(size_t i{0}; i < seeds.size(); ++i){
        glm::vec3 seed(ofRandom(-1.f, 1.f), ofRandom(-1.f, 1.f), 0.0);
        seed *= 400.f; // scale it if you like
        seeds.at(i) = seed;
    }
    
    // a smooth, continuous pline
    // start with the last point as a handle
    pline.curveTo(seeds.at(seeds.size() - 1), 100);
    // curveTo all of the seeds
    for(size_t i{0}; i < seeds.size(); ++i){
        pline.curveTo(seeds.at(i), 100);
    }
    // repeat the first two points to smoothly join the curve
    pline.curveTo(seeds.at(0));
    pline.curveTo(seeds.at(1));
    
    // add the first vertex to the end of pline if needed
//    pline.addVertex(pline.getVertices().at(0));
    
    // close the pline
    pline.close();
}

void ofApp::drawPline(){
    ofSetColor(ofColor::white);
    pline.draw();
    ofSetColor(ofColor::green);
    ofDrawCircle(pline.getVertices().at(0), 10.f);
    ofSetColor(ofColor::red);
    ofDrawCircle(pline.getVertices().at(pline.getVertices().size() - 1), 5.f);
}
1 Like

Something like this might work well too:

void ofApp::calcPline(){
    // clear the pline
    pline.clear();
    
    int num = 97;
    // sort some random x values
    std::vector<float> values(num);
    for(size_t i{0}; i < values.size(); ++i){
        values(i) = ofRandom(-1.0, 1.0);
    }
    std::sort(values.begin(), values.end());
    
    std::vector<glm::vec3> seeds(num);
    for(size_t i{0}; i < seeds.size(); ++i){
        glm::vec3 seed(values.at(i), ofRandom(-1.f, 1.f), 0.0);
        seed *= glm::vec3(400.f, 30.f, 0.f); // scale it
        seeds.at(i) = seed;
    }

    for(size_t i{0}; i < seeds.size(); ++i){
        pline.curveTo(seeds.at(i), 40);
    }
}
1 Like

https://openframeworks.cc/documentation/graphics/ofPolyline/#show_getPointAtPercent

Perfect!
This is exactly what I was looking for and tried to do. Thank you very much, saved me a lot of time.

M

1 Like

Also here are some thoughts related to a deleted post, which I’ll leave up.

ofPushMatrix()/ofPopMatrix() can be called multiple times in a draw cycle. This allows each thing to be translated, scaled, and rotated as needed.

Also, ofImage::draw() can take some additional arguments that will draw it to a different size. I think this is also the case for any thing with an ofTexutre. They can be drawn smaller or larger, with the same aspect ratio or stretched/squished:

float w = image.getWidth() * 0.25;
float h = image.getHeight() * 0.25;
// float h = image.getHeight() * 0.125; // squished in y
image.draw(0.f, 0.f, w, h);

Also, ofImage::drawSubsection() can sometimes be useful to just draw a subsection of an image. It takes quite a few arguments but I find it easy to use. This can help to avoid cropping and resizing, which actually changes an image and requires some resources.

yes, sorry I’ve deleted the post. Actually my question boiled down to: “how to upscale the image”… as I needed to zoom in the path without loosing quality. So finally I found the answer… I need to scale the random path to maximum… and then downscale it. That solves the image pixelisation problem.

1 Like