Manage tangents of ofPolyline command points (bezier curve)

hello,

First question : why there is no command points in ofPolyline ? do i have to work with an intermediate ofPath shape ?

Now the real question : i have a path from which i get an ofPolyline. I am using command points to deform the shape with the mouse. For a square, everything is ok. But for a circle for instance, when i move the control point, i loose the curve shape probable because the tangent in that control point is not interpolated. (see pics for details)

How can i modify the tangent at this point ? or interpolate the original tangent to the new control point position.

thanks

ofPolyline is just a collection of points, if you want complex primitives like beziers, arcs… you need to use ofPath

thanks @arturo

actually i used ofPath but i had issues scaling the shapes : scale pivot seems to be the origin point (0,0) so i was looking for some solution using ofPolyline specific methods like getCentroid2D(). But maybe i was doing something wrong…

ofPolyline seemed more adapted to complex manipulations as it also uses beziers, arcs and so on along with some useful methods that ofPath is missing and ofPath more adapted to drawing shapes. But if you suggest i keep using ofPath i find it better.

Anyway, the same problem occurs and i still don’t know how to manage it using ofPath

Any help appreciated.

thanks a lot.

I only see getters for tangents information but what is there a way to modify them ?

anyone please tell me if i need to find a way to compute tangents control points to adjust the curve or there exists a way or some tools i missed that would do the trick ?

thanks a lot

not sure i understand your question, do you need some interactive way to set the control points? both polyline and path allow to create beziers using control points

I don’t know, i just need to adjust the curve (or make it adjust itself if possible but i doubt it) when moving the control point. I imagine this is done by adjusting the tangents at this point. For instance concerning the blue shape, i need the shape to be curbed at the bottom control point and not that sharp angle.

I don’t know it it should be done in polyline or path level though but both is ok for my purpose…

what you need to move is the control points that define the path and regenerate it again, if you move the points in the polyline you’ve already lost the primitives that define it. you can do the same with polylines but in any case you need to recreate the full polyline again it’s not enough to modify the contents of the polyline since that’s only line segments

yes i am working with path. I also use polyline but for another purpose.
When i grab and move the control points, i am doing it in the shape’s path like so :

    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;
    }
}

where controls is :

vector<ofPath::Commands> controls;
controls = path.getCommands();

to define a bezier:

newPath.bezierTo(cp11, cp21, pt1);
newPath.bezierTo(cp12, cp22, pt2);

cp11 and cp21 are the tangent controls for pt1 and cp12 cp22 the tangent controls for pt2. if you move those with the mouse or programmatically you can change the tangent controls for the bezier that goes from pt1 to pt2.

sorry, i don’t understand cp11, cp12 etc…

cp11 is the control point 1 for point 1, cp21 control point 2 for point 1…

Ok thanks. This is what i came to understand, but (sorry for asking) where do i find those cp11 and cp12 ? i can access cp1 and cp2 but i can’t find how to move cp11 and cp12 ?

ok nevermind, i think i just understood your suggestion !
i get confused by cp11 as cp1 really exists as a property in the class and i was also looking for cp11 in somewhere in the class.

But it turns out that cp11 and cp21 in your example are actually command.cp1 and command.cp2 and pt1 is command.to am i right ?

Also, i did some simple tests just to be sure :

void ofApp::draw(){
    
    ofBackground(0);
    
    for (int i = 0; i < paths.size(); i++) {
        paths[i].draw();
        vector<ofPath::Command> commands;
        commands = paths[i].getCommands();
        for (int j = 0; j < commands.size(); j++) {
            switch (j) {
                case 0:
                    ofSetColor(255, 0, 0);
                    break;
                case 1:
                    ofSetColor(0, 255, 0);
                    break;
                case 2:
                    ofSetColor(0, 0, 255);
                    break;
                case 3:
                    ofSetColor(255, 0, 255);
                    break;
                case 4:
                    ofSetColor(255, 255, 0);
                    break;
                    
                default:
                    ofSetColor(255, 255, 255);
                    break;
            }
            ofCircle(commands[j].to, 8);
            ofCircle(commands[j].cp1, 5);
            ofCircle(commands[j].cp2, 5);
            ofLine(commands[j].to , commands[j].cp1);
            ofLine(commands[j].to , commands[j].cp2);
        }
    }
}

result is :

So yes, points have control points in common : for example, cp1 for point 2 is also cp2 for point 1 and cp2 for point 2 is also cp1 for point 3

so how would you make the control points to follow the point i grab with the mouse ?

1 Like

@arturo Just to be sure, does this representation seems correct to you ?

yes, i’m not sure which is the order for the control points, i’d say cp1 affects the previous bezier and cp2 the next

ok.

I now understand cp11 and cp12 (respectively commands[1].cp1 and commands[1].cp2) are the two control points attached to p1 that leads the line from p0 to p1

I used to think cp12 and cp21 where the two control points attached to p1