Draw shape contour from SVG

hello,

i have some trouble grabbing shape contours from an SVG file and drawing it properly !
note : i want those shapes to appear in an ofxBox2d world hence the ofxBox2dEdge.

I have an SVG with a square and a circle like follow :

here is my code :

.h

ofxBox2dEdge edgeLine;

.cpp

void ofApp::setup(){
     
    svg.load("1.svg");
    
 for (int i = 0; i < svg.getNumPath(); i++) {
    ofPath shape = svg.getPathAt(i);
    for(int j = 0; j < shape.getOutline().size(); j++) {
        ofPolyline line = shape.getOutline().at(j);
        edgeLine.addVertexes(line);
    }
 }
    edgeLine.setPhysics(0.0, 0.1, 0.7);
    edgeLine.create(box2d.getWorld());
}


void ofApp::draw(){
         edgeLine.draw();
}

by doing so here is what i end up with :

I can’t figure out what i am doing wrong

Anybody can help me ?

you need 1 ofxBox2dEdge per polyline, you can’t draw 2 shapes with only 1 contour

hi Arturo and thanks for your comment.

I think i don’t really understand what you mean…

as i understand, a box2dEdge contains the objects drawn in the box2d world. So i am trying to build this edgeLine by adding my shapes into it and then add this edgeLine to the box2d world.

an ofxBox2dEdge represents 1 contour only, a continuous line but you are trying to put 2 shapes on it so you get the square and at the end of it instead of closing the square the line goes to on to draw the circle. you need to have 2 ofxBox2dEdge or in general as many as shapes or even better contours you have in your svg.

ok ! thank you for the information.

Should i use ofxBox2dEdge to make a shape to interact with particles inside a box2d world or can i any shape (polyline of path) to the world directly ?

ok, so i managed to separate every shape using a vector of ofxBox2dEdge

.h

ofxBox2d box2d;
vector<shared_ptr<ofxBox2dRect> > blocks;
vector<shared_ptr<ofxBox2dEdge> > edges;

ofxSVG svg;

.cpp

void ofApp::setup(){
          
    box2d.init();
    box2d.createBounds();
    box2d.setFPS(60.0);
    box2d.setGravity(ofPoint(0, g, 0));
    
    svg.load("1.svg");
    
    for (int i = 0; i < svg.getNumPath(); i++) {
        ofPath shape = svg.getPathAt(i);
        for(int j = 0; j < shape.getOutline().size(); j++) {
            ofPolyline line = shape.getOutline().at(j);
            shared_ptr <ofxBox2dEdge> edge = shared_ptr<ofxBox2dEdge>(new ofxBox2dEdge);
            edge.get()->addVertexes(line);
            edge.get()->setPhysics(0.0, 0.1, 0.7);
            edge.get()->create(box2d.getWorld());
            edges.push_back(edge);
        }
    }
}

void ofApp::draw(){
    box2d.draw();
    
    for(int i = 0; i < edges.size(); i++) {
        edges[i].get()->draw();
    }
}

Now how i have some more questions !

    • shapes are not closed. is there a problem with the SVG ? or there is something i must do in the code ?
    • is it possible to grab the stroke color from the SVG and apply it to each respective shape ?

thanks a lot

I tried to add :

    shape.setPolyWindingMode(OF_POLY_WINDING_ODD);

some changes, but still not what i expected.

Looks like you just need to add the first point to the back of the polyline, since you want the shape to be closed.

After this line

ofPolyline line = shape.getOutline().at(j);

add

if( line.size() > 1 ) {
   line.addVertex( line.getVertices()[0] );
}

That will add a line from the last point to the first point and should result in a closed shape.

you need to check if the original polyline is closed with: line.isClosed() not sure if box2d contour allows you to set the contour as closed otherwise just add the first point again as @NickHardeman suggests

also path.getStrokeColor() will give you the color of every path.

take a look at the docs for ofPath: http://www.openframeworks.cc/documentation/graphics/ofPath.html and ofPolyline: http://www.openframeworks.cc/documentation/graphics/ofPolyline.html even just the names of the methods should give you a clue of how to get the info you need

ok ! thanks for the tip !

by the way i just tried

        line.close();

or

        edge.get()->close();

it doesn’t make any difference. how come ?

note : i created the shapes using the tools circle and square in illustrator, so i assume those shapes are closed.

The tip to add the first vertex works ok. But i wonder why the code above does nothing.

Hello,

Just some feedback.

Thanks to @NickHardeman and @arturo i managed to make it to work. thank you to both of you !

.h

ofxBox2d box2d;

vector<shared_ptr<ofxBox2dRect> > blocks;
vector<shared_ptr<ofxBox2dEdge> > edges;

vector<ofColor> shapeColors;
vector<float> strokeWidth;

ofxSVG svg;

.cpp

void ofApp::setup(){
     
    box2d.init();
    box2d.createBounds();
    box2d.setFPS(60.0);
    box2d.setGravity(ofPoint(0, g, 0));
    
    svg.load("1.svg");
    
    // get every distinct shapes from the SVG file one by one
    for (int i = 0; i < svg.getNumPath(); i++) {
        
        // grab a shape
        ofPath shape = svg.getPathAt(i);
        
        // store its color and stroke width
        shapeColors.push_back(shape.getStrokeColor());
        strokeWidth.push_back(shape.getStrokeWidth());
        
        // grab the outline from the shape
        for(int j = 0; j < shape.getOutline().size(); j++) {
            ofPolyline line = shape.getOutline().at(j);
            
            // if this is a closed shape, join the first and last points
            // to close the contour
            if(line.isClosed()) {
                line.addVertex(line.getVertices()[0]);
            }
            
            // create a box2dEdge from the shape to make it interact with
            // other ojects into the box2d world
            shared_ptr <ofxBox2dEdge> edge = shared_ptr<ofxBox2dEdge>(new ofxBox2dEdge);
            edge.get()->addVertexes(line);
            edge.get()->setPhysics(0.0, 0.1, 0.7);
            edge.get()->create(box2d.getWorld());
            edges.push_back(edge);
    }
}

//--------------------------------------------------------------
void ofApp::update(){
    box2d.update();
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofBackground(30);
    
    // draw shapes
    for(int i = 0; i < edges.size(); i++) {
        ofSetColor(shapeColors[i]);
        ofSetLineWidth(strokeWidth[i]);
        edges[i].get()->draw();
    }

    // draw physics particles
    for(int i = 0; i < blocks.size(); i++){
        ofSetColor(12, 220, 50);
        blocks[i].get()->draw();
    }
}

Just some more questions because i am interested in having some clues :

  • why, like i said in the previous post, the code line.close() has no effect ?

  • i used global variables to store color and width of the contours. It works but i wonder if there is a better way to get the values while drawing or store them with each shape. box2dEdge doesn’t seem to store or have access to these values.

thanks again for the great help

both sound like a bug in ofxBox2d, ofxBox2dEdge should take into account if the polyline it’s closed and it’s color i think.

btw you don’t need call get() in every shared_ptr like:

edge.get()->create(box2d.getWorld());

a shared_ptr works like a normal pointer for most things so you just need to call it like:

edge->create(box2d.getWorld());

Thanks @arturo !

maybe i should file a bug report to the ofxBox2d developper then.

And thanks for the input about shared_ptr
I don’t really get the specific purpose of a shared pointer as i am not dev, maybe something too subtle for my skills !

I just followed the ofxBox2d recommandation and the syntax used in all the ofxBox2d examples… but yeah, using it as a normal pointer does the job so i will use it like that.

Thanks for all the great and useful information