Saving Screen to Svg, problems with rotations / translations

I am saving my screen to SVG, but it is not saving what is visually shown on the screen. For example if I do:

BeginSVGsave
Camera.begin()
Object.draw()
Camera.end()
EndSVGsave

The svg object is just ortho top down view. Is there a way to apply the camera transforms to the scene when saving the svg? Is there another method?

indeed looking at cairo renderer source we see many functions simply ignore the Z (ex: triangle ) while some other go through a transform() call. however a quick try of exporting the 3DPrimitives example does “something” but not really understandable:

here is a discussion and a workaround: ofEasyCam save as pdf (but managing at the vertex level is not necessarily practical depending on what you’re doing?). more background: Rendering ofCamera to pdf - #2 by arturo

if you can manage your geometry through ofMesh it allows PLY export, which can be loaded in blender and exported as SVG as projected by a camera.

Yeah I saw those examples and was thinking about a different approach.

Current approach: I do a lot of vector art, then drawing on a plotter. There are a lot of python tools for this, but I hate python. ;D As of now, my current process is to make a bunch of widgets as addons. These widgets could be 1000 circles, points, lines , etc that do something cool. From a new ofApp, ill take in all of the addons to create some custom print, save as an svg (thats the hope), then plot it. Caveat is that all the widgets are in 3D space, and I use the cam to rotate, zoom, etc to get different views to plot in 2D. Most of the widgets have a draw function that draws straight to the screen. of1000circs.draw(), that use ofDrawCircle() type functions, for example.

Is there a better method to create the widgets that would give me axis to the verts?

Who thought drawing lines would be so complicated? :smiley:

to get vertices you may draw with ofPath, which has ::getTesselation(), which returns an ofMesh, which has ::getVertices().

i dont have much experience with plotters (nor CNC/etc) but beyond the camera coordinates transforms, i understand it is difficult to convert identically from the openGL view to g-code instructions as openGL approximates curves as a sequence of straight lines, while g-code has true arcs.

perhaps instead of grabbing the render you could write in parallel native g-code to a txt file and skip the SVG? so when calling ofDrawCircle for your screen preview, your also transform the coordinates of the circle with easyCam.worldToScreen() and generate the corresponding g-code arcs? maybe lots of work.

or maybe render to a large FBO and recover the generated curves in a software that does that…

Tried the tessellation + get vertices strategy but got undesired results. I ended up redoing my widget to draw using ofPolylines. Then, if a flag is set, it will do the worldtoscreen transform on the points, otherwise it draws them normally. This works, but is going to be a pain if I have to set every the flag for every widget in the draw function. I also have to pass a camera pointer from the main app to the widget. It works but kinda nasty.

Drawing from the main app looks like this:

if (save_as_svg == true)
{
    Omaj.pointstoscreen=true;
    Omaj.worldcam = &C;
    ofBeginSaveScreenAsSVG("test.svg",false,true,ofRectangle(0,0,1000,1000));
    C.begin();
    Omaj.draw();
    ofEndSaveScreenAsSVG();
    C.end();
    Omaj.pointstoscreen=false;
    save_as_svg = false;
}
else
{
    C.begin();
    Omaj.draw();
    C.end();
}

Draw function inside the widget looks like this.

    if (pointstoscreen == true)
    {
        ofPolyline ppl;
        std::vector<glm::vec3> verts = pl.getVertices();

        for( int q = 0; q < verts.size() ; q++)
        {
            ppl.addVertex(worldcam->worldToScreen(verts.at(q)));
        }
        ppl.draw();
    }
    else
    {
        pl.draw();        
    }

to make thing less tedious you could flip the design: assuming all your drawing objects share a base Omaj class having an instance of ofPolyline, you could store your objects in a vector, and iterate the vector to retrieve all the polylines in a loop that performs the 2Dprojection or normal draw:

// app.h
std::array<std::unique_ptr<Omaj>,1000> omajes{};
// app.cpp
if (save_as_svg == true) {
    // setup svgwriter...
    for (const auto & omaj: omajes) {
        ofPolyline ppl;
        std::vector<glm::vec3> verts = omaj->pl.getVertices();
        // iterate vertices, worldToScreen, etc...
        ppl.draw()
    }  
    // teardown svgwriter...
} else {
    for (const auto & omaj: omajes) {
        omaj->pl.draw();
    }
}

Hmmmmmmmmmm all widgets will be a vectors of polylines. Maybe I need a widget base class. Interesting I might do this. Thanks!