Strategy to draw curves w/ ofPolyline, change points, keeping the curves

#1

Hi OF, putting this in beginners forum.

I need to draw curves and use them as path for moving elements all along them.
This is why I chose ofPolyline (I don’t need all ofPath features, I think)

At the beginning, I generate n ofPoints and I put them in my ofPolyline using curveTo() (which is probably NOT the right way).

I draw that.
nice (except for the first and last points because I put them using addVertex() and from 0 to 1 and from n-2 to n-1 there are lines and not curves)

Then I want to move some points from a position on the screen to another.
I do that and as soon as I did that, the draw mode from and to the point I changed are lines and no more curves.

I suspect a bad strategy by me…

Any good practices with that ?

Thanks a lot
Julien

How can I smooth output polylines?
#2

ofPolyline is a collection of points and even if you add “curves” to it, those are just turned into points before they are added to the polyline so in the end, it will always be a set of points.

To keep things smooth, sometimes I use the resampling and smoothing of the polyline object to “curve” it, ie:

myLine = myLine.getResampledBySpacing(1);
myLine = myLine.getSmoothed(3);

but this just doing smoothing, which curves the line…

another option is to look at ofPath, which is more of a higher level representation of a shape, where you can have higher level commands like a curve and they are stored internally as curves. You can then get the outline of the shape as a set of polylines (points) when you want to draw it, etc.

1 Like
#3

perfect timing :wink:

thanks a lot Zach.
I don’t know… I’m going to test ofPath right now.

#4

I need an easy way to move objects all along the path and getPointsAtPercent() is perfect for that

I don’t know what could be a good strategy.
maybe a loop like:

  • moving points
  • destroying the polyline
  • feed the polyline with the new points array
  • calculating the polyline and drawing it
  • draw my moving object at the right percent of the polyline
#5

hi,

i am not an expert and i don’t know if this is the right way to achieve it, but this is how i update shapes while dragging points with the mouse using bezier command points :

   void Shape::updatePath() {
    
    ofPath newPath;
    newPath = path;     // save shape properties before editing path
    newPath.clear();    // clears path but keeps properties like color, stroke...
    
    // build new path according to actual path's control points properties
    for (int i = 0; i < controls.size(); i++) {
        switch (controls[i].type) {
            case ofPath::Command::moveTo:
                newPath.moveTo(controls[i].to);
                break;
            case ofPath::Command::lineTo:
                newPath.lineTo(controls[i].to);
                break;
            case ofPath::Command::curveTo:
                newPath.curveTo(controls[i].to);
                break;
            case ofPath::Command::bezierTo:
                newPath.bezierTo(controls[i].cp1, controls[i].cp2, controls[i].to);
                break;
            case ofPath::Command::quadBezierTo:
                newPath.quadBezierTo(controls[i].cp1, controls[i].cp2, controls[i].to);
                break;
            case ofPath::Command::arc:
                newPath.arc(controls[i].to, controls[i].radiusX, controls[i].radiusY, controls[i].angleBegin, controls[i].angleEnd, controls[i].arc);
                break;
            case ofPath::Command::arcNegative:
                newPath.arc(controls[i].to, controls[i].radiusX, controls[i].radiusY, controls[i].angleBegin, controls[i].angleEnd, controls[i].arcNegative);
                break;
            case ofPath::Command::close:
                newPath.close();
                break;
                
            default:
                break;
        }
        path.clear();
        path = newPath;
        path.flagShapeChanged();
    }
}
#6

Hi @gluon,
as zach said there’s the ofPath option. you can call the getCommands() from it which will give you a vector of ofPath::commands which are actually the commands and parameters stored whenever you called moveTo(…) or bezierTo(…). you can access those and modify them. I’ve been using this method a lot lately. Whenever you modify the ofPath through the commands, its polylines will get recalculated that you can access through the getOutline() method, which returns a vector object,then use the getPointForPercent() method on these.
take a look at the following.

ofApp.h

#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{

    public:
        void setup();
        void update();
        void draw();

        void mouseDragged(int x, int y, int button);

    ofPath path;
    
    float pct;
    
        
};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

    
    path.moveTo(ofPoint(400,400));
    path.bezierTo(ofPoint(500,500), ofPoint(600,300), ofPoint(400,600));
    path.setFilled(false);
    path.setStrokeWidth(3);
}

//--------------------------------------------------------------
void ofApp::update(){

    pct+=0.01;
    if (pct >1) {
        pct = 0;
    }
    
}

//--------------------------------------------------------------
void ofApp::draw(){
    path.draw();
    
    ofSetColor(ofColor::red);
    
    ofDrawCircle(path.getOutline().back().getPointAtPercent(pct), 20);
    
}
//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
    path.getCommands().back().to.set(ofPoint(x,y));
}

Hope it helps.
best!

#7

It absolutely helps.
I don’t know the best practice in that case: using ofPolyline or ofPath.
Actually, in my case, I’d need to change all points of my path, sometimes (using the animated/smoothed way… the other thread where you answered me :slight_smile: )

Now, another “issue” for me: when strokeweight is greater than even 2 or 3, joints are not cute. I saw some posts about that… checking now.

Thanks again.

#8

I see. in this case I think that ofPolyline is a good choice. Where are the new points comming from? are you adding these new points to the polyline through lineTo, bezierTo,…, or these points are sumply line vertices.
In case of the latter, Maybe a better solution is to use a ofMesh or ofVboMesh.

The stroke “issue” is because openGL. There are several methods to work it around. I made an addon some years ago that addressed this issue called ofxFatLines. It kinda works nicely but I it fails when you have sharp angles. It is based on the very basic idea of VASE renderer, which now a days seems to be much more mature than my addon. also, yo might want to try ofxShivaVG. it renders 2D stuff really nicely.

#9

but the real problem for me now is combining this (getcommand() with ofPath method I mean) with polyline morphing

I think I have to port the code for animating the morphing between ofPolyline to ofPath… should be doable :slight_smile:

#10

Hi @gluon
in this case, where you are just drawing lines, you can think of ofPath as a kind of buffer of commands to create polylines, which will actually create the points of the polyline only when needed. So you actually dont need to port the code for morphing, simply update the morphed polylines when its according ofPath changes.

#11

Hi,
A very basic one, with ofPolyline and addVertex.
It clear the vertices and compute them again in update.

ofApp.h

class ofApp : public ofBaseApp{

public:
   	void update();
	void draw();

	ofPolyline polyline;
	float elementPositionPercent = 0.f;
	ofPoint elementPosition;
};

ofApp.cpp

#include "ofApp.h"

void ofApp::update()
{
	// Curve center
	float cx = ofGetWidth() * 0.5f ;
	float cy = ofGetHeight() * 0.5f ;
	float cz = 0.f ;

	// Curve radius
	float rx = cx * 0.5f ;
	float ry = cy * 0.5f ;
	float rz = rx ;

	// Angle animation
	float dx = ofGetElapsedTimef() * 0.3f ;
	float dy = -ofGetElapsedTimef() * 0.3f ;
	float dz = ofGetElapsedTimef() * 0.01f ;

	// Push vertices to polyline
	polyline.clear() ;
	for( float angle = 0.f, angleMax = 2.f * PI ; angle < angleMax ; angle += 0.005f )
	{
		ofPoint p ;
		p.x = rx * cos( dx + angle * 15.f ) ;
		p.y = ry * sin( dy + angle * 23.f ) ;
		p.rotateRad( dz + angle, ofPoint( 1.f, 0.f, 0.f ) ) ;
		p.x += cx;
		p.y += cy;
		p.z += cz;
		polyline.addVertex( p ) ;
	}

	// Animate the disk on the curve
	elementPositionPercent += 0.0002f ;
	if( elementPositionPercent > 1.f ) elementPositionPercent = 0.f ;
	elementPosition = polyline.getPointAtPercent( elementPositionPercent ) ;
}

void ofApp::draw()
{
	polyline.draw() ;
	ofDrawCircle( elementPosition, 15.f ) ;
}

src.zip (2.7 KB)

Not sure this help :slight_smile:

#12

Thanks @lilive
Actually, I tested this kind of implementation myself. Clearing and feeding again in update()

Don’t Know about efficiency or cpu cost in each case (ofPath and ofPolyline)

Probably similar.