vg-renderer icon indicating copy to clipboard operation
vg-renderer copied to clipboard

Error in vertex calculation

Open DarkContact opened this issue 3 years ago • 3 comments

I tried to make a minimal example to demonstrate the problem.

Let's take a bezier curve made in inkscape: image If I try render with vg I see what: image

Code:

vg::beginPath(vgContext);
float x = 84.889012f; 
float y = 71.749671f;
vg::moveTo(vgContext, x, y);

vg::cubicTo(vgContext,
          x - 2.59f, y + 3.04f,
          x - 6.31f, y + 4.89f,
          x - 10.12f, y + 5.88f);
x = x - 10.12f; y = y + 5.88f;

vg::cubicTo(vgContext,
          x - 3.62f, y + 1.4f,
          x - 6.86f, y + 3.58f,
          x - 10.37f, y + 5.21f);
x = x - 10.37f; y = y + 5.21f;

vg::cubicTo(vgContext,
          x - 2.07f, y + 1.0f,
          x - 4.25f, y + 1.78f,
          x - 6.24f, y + 2.96f);
x = x - 6.24f; y = y + 2.96f;

vg::cubicTo(vgContext, 
          x - 0.509996f, y + 0.1f,
          x + 0.96f, y - 0.67f,
          x + 1.29f, y - 0.85f);
x = x + 1.29f; y = y - 0.85f;

vg::cubicTo(vgContext,
          x + 2.62f, y - 1.3f,
          x + 5.35f, y - 2.37f,
          x + 7.92f, y - 3.78f);
x = x + 7.92f; y = y - 3.78f;

vg::cubicTo(vgContext,
          x + 3.19f, y - 1.6f, 
          x + 6.23f, y - 3.58f,
          x + 9.71f, y - 4.52f);
x = x + 9.71f; y = y - 4.52f;

vg::cubicTo(vgContext,
          x + 2.57f, y - 0.72f,
          x + 4.94f, y - 2.11f,
          x + 6.79f, y - 4.03f);
x = x + 6.79f; y = y - 4.03f; 

vg::cubicTo(vgContext,
          x + 0.34f, y - 0.29f,
          x + 0.58f, y - 0.71f,
          x + 1.02f, y - 0.87f);

vg::closePath(vgContext);
vg::strokePath(vgContext, vg::Colors::Black, 4.0f, vg::StrokeFlags::ButtMiterAA); // Error also with vg::StrokeFlags::ButtMiter

Also the problem is easy to reproduce it to output svg on a small scale (0.1f or less) with AA enabled: image image

In an imperial way, I tried to change the kMaxExtrusionScale parameter here:https://github.com/jdryg/vg-renderer/blob/4f1bfa7e57561728fc5fa36476cf550bac885663/src/stroker.cpp#L45 and there are fewer chaotic lines. But there are small inaccuracies elsewhere. Any ideas on how to fix this?

DarkContact avatar Aug 23 '21 13:08 DarkContact

This must be related to miter limit which isn't supported in vg-renderer (see Readme)

Inkscape (and all proper vector graphics renderers) support miter limit which will convert thin long miters into bevel joints. vg-renderer doesn't support miter limit in order to simplify stroke calculations. It was a decision I made very early and never got back into fixing it. I don't really know how much such calculations will affect runtime performance.

Unfortunately, I cannot think of an easy way to fix this, other than rewriting all stroker functions in a way to include this kind of checks and convert the problematic stoke corners into bevels.

EDIT: Added link to stroke-miterlimit SVG attribute.

Not supporting miter limit in vg-renderer is the same as setting the limit to a large value in all other renderers. You can check this by altering stroke-miterlimit in your SVG file and viewing it in your browser. See example in the linked MDN page.

jdryg avatar Aug 23 '21 14:08 jdryg

Thanks for the answer. But it seems to me that the problem is not only miter limit. I have attached some images for a better understanding of the problem. In the turquoise rectangles, I have highlighted the problem areas. As the kMaxExtrusionScale ratio increases, there are fewer errors. However, on smaller scales, kMaxExtrusionScale has to be done more in order to achieve acceptable quality. It seems that kMaxExtrusionScale should not be constant. What do you think on this?

svg_010_area svg_100_area image image

DarkContact avatar Aug 23 '21 16:08 DarkContact

Even path fills suffer from the same problem as strokes without a miter limit when using AA. This is because the same algorithm is used for calculating AA fringes. Fringes as half-width strokes around a filled path. See https://github.com/jdryg/vg-renderer/blob/master/src/stroker.cpp#L867

kMaxExtrusionScale is a hack to limit the amount of extrusion of those problematic cases. The correct way to handle it is to dynamically limit the extrusion and convert those corners into bevels, even for the AA fringes. Problem with dynamically produced bevels is that the amount of vertices isn't known beforehand so extra allocations are necessary inside the inner loop as well as extra checks.

Hope that makes sense. You can check this is due to AA if you disable AA in all the problematic cases you shown above. A polygon fill without AA doesn't introduce any more vertices than those produced by the path tesselation. AA on the other hand produces extra vertices around the polygon with an alpha value of 0 in order to smoothly fade the edges.

jdryg avatar Aug 24 '21 05:08 jdryg