memononen / nanovg

Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations.
zlib License
5.15k stars 770 forks source link

Edges of the curve are drawn at an angle #650

Open a7951396 opened 1 year ago

a7951396 commented 1 year ago

I entered the data from the svg file for the bezier curve, but the edges of the curve are drawn at an angle, uneven. But in the browser the svg file is drawn with straight ends. And the cairo library also draws straight. But gdi/gdi+ also draws unevenly. Is everything smooth in adobe illustrator too. What could be the problem? `

    nvgBeginPath(vg);
    float x = 168.f;
    float y = 37.f;
    nvgMoveTo(vg, 168.f, 37.f);
    nvgBezierTo(vg, 0 + x, 35.4f + y, 39.4f + x, 64 + y, 88 + x, 64 + y);
    nvgStrokeColor(vg, nvgRGBA(0, 0, 0, 255));
    nvgStrokeWidth(vg, 32); 
    nvgStroke(vg);`

svg code: `<?xml version="1.0" encoding="utf-8"?> <svg version="1.2" baseProfile="tiny" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 510 512" overflow="visible" xml:space="preserve">

` 2022-10-27_19-29-24

And this is what the curve looks like in the browser: 2022-09-12_23-35-32

mulle-nat commented 1 year ago

Maybe nanovg is correct and the browser isn't ? :thinking: I don't know. If one were to extend the curve a little I assume flatness could be achievable. Maybe the browser isn't even using bezier for curves but something else. I am sure this isn't helping much, but since noone answered so far...

a7951396 commented 1 year ago

I drew this curve in illustrator and all the edges are straight

mulle-nat commented 1 year ago

So maybe complain to Adobe ?

I drew this in nanovg and all the lines are straight: image

static void   drawStuff( CALayer *layer, CGContext *context)
{
   struct NVGcontext    *vg;

   vg = CGContextGetNVGContext( context);
   nvgBeginPath(vg);

   nvgRect( vg, 100, 100, 300, 300);
   nvgStrokeColor(vg, nvgRGBA(0, 0, 128, 64));
   nvgStrokeWidth(vg, 1);
   nvgStroke(vg);

   nvgBeginPath(vg);
   nvgMoveTo(vg, 100, 100);
   nvgArcTo(vg, 100, 400,
                400, 400,
                300);
   nvgStrokeColor(vg, nvgRGBA(0, 0, 0, 255));
   nvgStrokeWidth(vg, 32);
   nvgMiterLimit( vg, 20);
   nvgStroke(vg);

   nvgBeginPath(vg);
   nvgRect( vg, 450, 100, 300, 300);
   nvgStrokeColor(vg, nvgRGBA(0, 0, 128, 64));
   nvgStrokeWidth(vg, 1);
   nvgStroke(vg);

   nvgBeginPath(vg);
   nvgMoveTo( vg, 750, 100);
   nvgBezierTo( vg, 750, 100 - (0.552 * -300), 450 - (0.552 * -300), 400, 450,400);
   nvgStrokeColor(vg, nvgRGBA(0, 0, 0, 255));
   nvgStrokeWidth(vg, 32);
   nvgMiterLimit( vg, 20);
   nvgStroke(vg);
}
a7951396 commented 1 year ago

draw in adobe

a7951396 commented 1 year ago

I plan to draw in a vector graphics editor and then parse the data from the svg file and draw in opengl

a7951396 commented 1 year ago

I tried using Corel Draw, the edges are also uneven. 2022-10-29_11-03-30

memononen commented 1 year ago

I think the problem is that the end cap is aligned to the tessellated line, not the tangent of the curve. I've had to deal with this issue in other projects too when you have really wide lines.

Imagine that the curve above would be approximated with just 4 points, and the end caps were drawn perpendicular to the first and last segment.

The solution is to specifically handle the direction of the first and last edge when drawing open shapes.

a7951396 commented 1 year ago

I’m not sure that I understood something, since I don’t have enough experience in computer graphics, that’s why I’m looking for a ready-made library

kobalicek commented 1 year ago

Nanovg rendering is incorrect in this case. The caps must be straight, see this Blend2D rendered sample:

image

After applying the x/y translation the input path becomes:

  moveTo(168, 37);
  cubicTo(168, 72.4, 207.4, 101, 256, 101);

The X of moveTo and first cubicTo control point are the same, which means they form a vertical line, which must be used to calculate the angle of start cap. This means that the start cap must be strictly horizontal. The same happens at the end - the last control point and the cubic end point share the same Y coordinate.

If you want to play with that you can drop the following code to Blend2D fiddle ( https://fiddle.blend2d.com ):

BLImage render(const BLContextCreateInfo& cci) {
  BLImage img(500, 140, BL_FORMAT_PRGB32);
  BLContext ctx(img, cci);

  ctx.setFillStyle(BLRgba32(0xFFFFFFFFu));
  ctx.fillAll();

  BLPath p;
  float x = 168.f;
  float y = 37.f;
  p.moveTo(168.f, 37.f);
  p.cubicTo(0 + x, 35.4f + y, 39.4f + x, 64 + y, 88 + x, 64 + y);
  ctx.setStrokeWidth(32);
  ctx.setStrokeStyle(BLRgba32(0xFF000000));
  ctx.strokePath(p);

  return img;
}