bugGNU Octave - Bugs: bug #58429, Export of plotted circles to SVG...

 
 

bug #58429: Export of plotted circles to SVG could use SVG circle primitive

Submitted by:  None
Submitted on:  Fri 22 May 2020 07:58:59 PM UTC  
 
Category:  Plotting with OpenGL Severity:  3 - Normal
Priority:  3 - Low Item Group:  Other
Status:  None Assigned to:  None
Originator Name:  Originator Email:  -email is unavailable-
Open/Closed:  Open Release:  dev
Operating System:  GNU/Linux

Add a New Comment (Rich Markup)
   

You are not logged in

Please log in, so followups can be emailed to you.

 

( Jump to the original submission)

Wed 27 May 2020 10:04:21 AM UTC, comment #10: 

I am currently in the process of adding support for a latex interpreter, and for that purpose I had to add support for "path" and "rect" elements in svgconvert. 

>> That means, there is no way to have Bezier curves in svg (or ps or pdf, for that matter) via gl2ps?


gl2ps is only able to retrieve lines and triangles from the feedback buffer, but you can still push preformated svg elements to the output using gl2psSpecial.

For example, I am currently in the process of adding support for a latex interpreter. For printing, I directly pass svg "path", "rect", and "using" elements to gl2ps. After adding support for "path" and "rect" and "using" elements in svgconvert, I am now able to print to svg but also pdf, eps... 

Pantxo Diribarne <pantxo>
Project Member
Tue 26 May 2020 07:56:56 PM UTC, comment #9: 

As I said, I don't know anything about OpenGL. I just googled for "opengl bezier" and found some discussions where Bezier curves are drawn, so came to my conclusion. But seemingly I was too quick there. If I google for "gl2ps bezier" it does not look so good, so that probably settles it. That means, there is no way to have Bezier curves in svg (or ps or pdf, for that matter) via gl2ps? That is quite a pity, as vector graphics only in terms of lines are necessarily quite crippled (and I am sure any standard like Postscript or PDF would have vanished quickly from the market without them), just think about fonts.

Michael Leitner <mleitner>
Tue 26 May 2020 01:14:27 PM UTC, comment #8: 

@mleitner:

>> ... And it seems that OpenGL handles cubic Bezier curves natively.


Natively, OpenGL only defines three line primitives: GL_LINES, GL_LINE_STRIP and GL_LINE_LOOP basically defined as a succession of end points:

  https://www.khronos.org/opengl/wiki/Primitive#Line_primitives

We have an open bug about moving away from those primitives since we also use GL_LINE_STIPPLE which is now deprecated: bug #46739.

Pantxo Diribarne <pantxo>
Project Member
Tue 26 May 2020 07:29:33 AM UTC, comment #7: 

I don't know anything about OpenGL, but I know about SVG: as far as I know, a <circle> element is typically rendered by the renderer converting it first to a cubic Bezier curve, with four segments to make up the full circle, and then using the general routines for Bezier curves. You see the resulting choice of control points if you draw a circle in Inkscape and convert it to a path. That said, in a renderer that uses such an approach, there would absolutely be no visual difference if Octave were to output such a Bezier curve for circular markers. In particular, the initial complaint about "hairy" or "corroded" appearance would be solved. And it seems that OpenGL handles cubic Bezier curves natively.

Further, it could make the code simpler: you would not need to decide how many points you have to use to make the plot satisfactorily smooth (depending on the size of the marker), but always use four Bezier segments and let the library think about how this is best rendered, both for on-screen plotting and for plotting into a file. Of course, this is still much less efficient than directly outputting <circle> elements, but this is something that OpenGL seemingly does not know about. However, perhaps it is possible to use relative instead of absolute positions in the svg output, then it would at least compress to the same size (as the position of the marker is always encoded by the first control node, while the remaining 12 nodes/control points that make up the rest of the path statement are always at the same relative positions). And this would then indeed be amenable to postprocessing.

Michael Leitner <mleitner>
Tue 26 May 2020 03:24:32 AM UTC, comment #6: 

Testing a bit more with Matlab, it seems that the outline of a marker is done with a <path> object in SVG and only if the marker is filled does it use a <circle> element for the fill.

There is no distinction between 2-D and 3-D, it results in the same file.

It seems if we really wanted to support this well we could use a circle element for the markeredge as well.  But, at least we're pretty much Matlab compatible with the existing implementation.

Rik <rik5>
Project Administrator
Mon 25 May 2020 08:09:10 PM UTC, comment #5: 

I don't think it is possible to post-process a bunch of triangles and lines to guess it they were intended to look like a circle or a polygon.

Another approach could be, in gl2ps_renderer, to overload opengl_renderer::draw_marker, as we already do for opengl_renderer::init_marker, and directly dump svg code using gl2psSpecial as we do for text objects.

I guess this won't work reliably for 3D scenes though.

Pantxo Diribarne <pantxo>
Project Member
Mon 25 May 2020 06:36:47 PM UTC, comment #4: 

If it helps, the marker drawing code for 'o' is in gl-render.cc at line 4676.  Reproduced below.

case 'o':
  {
    int div = static_cast<int> (M_PI * sz / 4);
    if (! (div % 2))
      div += 1;               // ensure odd number for left/right symmetry
    div = std::max (div, 5);  // ensure at least a few vertices are drawn
    double ang_step = M_PI / div;

    m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
    for (double ang = 0; ang < 2*M_PI; ang += ang_step)
      m_glfcns.glVertex2d (sz/2*cos (ang), sz/2*sin (ang));
    m_glfcns.glEnd ();
  }

 
Rik <rik5>
Project Administrator
Mon 25 May 2020 04:27:38 AM UTC, comment #3: 

Let me work on it offline this week. I'd like at least the plot primitives to be covered as SVG primitives (circles and ideally squares, maybe others). My current workaround code is very ad hoc: it simply takes the arithmetic mean of the 10 points to use as the center, and the Euclidean distance of that center from the points as the radius. There's a check that if the center-to-point distance displays too much variance then it's not a circle to begin with. I also have code to write SVG circle primitives from data directly without going through plot and gl2ps. But that code only works for my limited case.

I can do it as a post processor after gl2ps. That would keep the architecture constant.

Anonymous
Mon 25 May 2020 01:08:44 AM UTC, comment #2: 

I do note that Matlab produces a circle primitive in svg.  However, it uses an entirely different approach using a Java library called Batik for printing svg.  Not something Octave can reproduce.

Rik <rik5>
Project Administrator
Mon 25 May 2020 12:49:33 AM UTC, comment #1: 

Although seemingly simple, this would be difficult to implement.

Octave uses an external library, gl2ps, for printing which translates the OpenGL commands rendered on the screen into one of a few formats including SVG.  OpenGL itself has no circle primitive, so all onscreen implementations approximate the circle with a polygon (filled) or polyline (unfilled) object.  Thus, per the architecture of the implementation, everything is working correctly as the onscreen representation has been exactly reproduced in the output format.

If you were interested in working on this I would probably code some sort of post-processor for the text svg that attempts to determine when a polygon is actually representing a circle and then calculates the necessary parameters to use an SVG circle primitive.

Rik <rik5>
Project Administrator
Fri 22 May 2020 07:58:59 PM UTC, original submission:  

This may or may not be a bug, but it is non-intuitive behavior.

Input is any of these plot statements:

[xx,yy] = meshgrid(1:3,1:3);
plot(xx(:), yy(:), 'ko', "markersize",20); axis equal; axis off; saveas (gcf, "foo1.svg"); close all
plot(xx(:), yy(:), 'k.', "markersize",20); axis equal; axis off; saveas (gcf, "foo2.svg"); close all
plot(xx(:), yy(:), 'ks', "markersize",20); axis equal; axis off; saveas (gcf, "foo3.svg"); close all

Output inside SVG is like this:

<polygon fill="#000000" shape-rendering="crispEdges" points="91.4142,281.458 89.7475,282.669 87.6874,282.669 86.0207,281.458 85.3841,279.499 86.0207,277.54 87.6874,276.329 89.7475,276.329 91.4142,277.54 92.0508,279.499"/>

Ideal SVG output would be something like (for solid circles):

<circle fill="#000000" r="3.33" cx="88.717" cy="279.5" />

with similar statements for the other shapes in the format arguments accepted by plot.

This makes a difference in the quality of the resulting SVG, with the large polygon statement looking "hairy" or "corroded" instead of smooth because of the long list of vertices.

This has been worked around in code by directly writing SVG output to file instead of invoking saveas(). Is this worth improving the SVG export for?

Anonymous

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by mleitner (Posted a comment)
  • -email is unavailable- added by pantxo (Posted a comment)
  • -email is unavailable- added by rik5 (Posted a comment)
  •  

    Do you think this task is very important?
    If so, you can add your encouragement to it.
    This task has 0 encouragements so far.

    Only project members can vote.

     

     

     

    Follow 4 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2020-05-25 rik5 CategoryNone => Plotting with OpenGL
        Priority5 - Normal => 3 - Low
        Release6.0.90 => dev
        SummarySVG export could be better => Export of plotted circles to SVG could use SVG circle primitive

    Back to the top


    Powered by Savane 3.5