Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

curve_to renders incorrectly #308

Open
morpho-matters opened this issue Jan 6, 2023 · 1 comment
Open

curve_to renders incorrectly #308

morpho-matters opened this issue Jan 6, 2023 · 1 comment

Comments

@morpho-matters
Copy link

Pycairo seems to be incorrectly rendering certain cubic Bezier splines. The joins are incorrect and the line width changes.

bug

Here is the code to reproduce the effect:

import cairo, ctypes

pts = [(5.1502081, 3.69203566),
    (5.15072707, 3.88650738), (4.98411476, 3.88754198), (4.82523962, 3.88857803),
    (4.82529145, 3.88868164), (1.92531232, 3.88878525), (1.92536413, 3.88888889),
    (1.72264052, 3.88888889), (1.42242387, 3.88888889), (1.01697664, 3.45713401)
    ]

def main():
    width, height = 600, 600
    renderData = (ctypes.c_ubyte * (width*height*4))()
    stride = width*4
    surface = cairo.ImageSurface.create_for_data(renderData, cairo.FORMAT_ARGB32,
        width, height, stride
        )
    context = cairo.Context(surface)
    context.set_line_join(cairo.LINE_JOIN_ROUND)
    context.set_source_rgba(0,0,0,1)
    context.set_operator(cairo.OPERATOR_SOURCE)
    context.paint()

    context.scale(100, 100)

    context.move_to(*pts[0])
    for n in range(1, len(pts), 3):
        x1,y1 = pts[n]
        x2,y2 = pts[n+1]
        x,y = pts[n+2]
        context.curve_to(x1,y1, x2,y2, x,y)
    context.set_line_width(0.1)
    context.set_source_rgba(1,1,1,1)
    context.stroke()
    context.get_target().write_to_png("bug.png")

main()

This is running on Python 3.8.1 on Windows 7 with Pycairo 1.23.0.

I suspect this has something to do with how the middle segment is approximately linear (two of the tangent points are almost exactly coincident with their corresponding positional points) but I don't really know. Any help appreciated!

@psychon
Copy link

psychon commented Sep 30, 2023

Tried to minimize this a bit. Dunno if this is much better than the original. I guess cairo-the-C-library has some kind of over/underflow somewhere.

import cairo, ctypes, math

def main():
    width, height = 600, 600
    surface = cairo.ImageSurface(cairo.FORMAT_RGB24, width, height)
    context = cairo.Context(surface)

    context.scale(100, 100)

    # The actual curve that breaks
    context.curve_to(4.82529145, 3.88868164,
       1.92531232, 3.88878525,
       1.92536413, 3.88888889)

    # Just for comparison, a "proper" line with this line width
    if False:
        context.rel_line_to(0, -1)

    context.set_line_width(0.1)
    context.set_source_rgb(1,1,1)
    context.stroke()

    # Show the control points for the curve
    if False:
        for i, (x, y) in enumerate(
                [(4.82529145, 3.88868164),
                 (1.92531232, 3.88878525),
                 (1.92536413, 3.88888889)]):
            color = [0, 0, 0, 0.5]
            color[i] = 1
            context.set_source_rgba(*color)
            context.arc(x, y, 0.05, 0, 2*math.pi)
            context.fill()

    context.get_target().write_to_png("bug.png")

main()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants