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

Figure glitches CairoMakie backend #1843

Closed
Lightup1 opened this issue Apr 27, 2022 · 8 comments · Fixed by #1844
Closed

Figure glitches CairoMakie backend #1843

Lightup1 opened this issue Apr 27, 2022 · 8 comments · Fixed by #1844
Labels
CairoMakie This relates to CairoMakie, the vector backend for Makie based on Cairo.

Comments

@Lightup1
Copy link

As shown in figure, there are glitches when you try to thicken the line.

CairoMakie

linewidth = 1

image

linewidth = 2

image

linewidth = 5

image

These glitches can be resolved when you define large resolution.
fig=Figure(resolution=(4000,3000))

image

GLMakie

linewidth = 5

image

@SimonDanisch SimonDanisch added the CairoMakie This relates to CairoMakie, the vector backend for Makie based on Cairo. label Apr 27, 2022
@asinghvi17
Copy link
Member

asinghvi17 commented Apr 27, 2022

With CairoMakie, could you try:

save("fig_highres.png", your_figure; px_per_unit=5)
save("fig.pdf", your_figure; pt_per_unit=1)

and upload them here?

It looks like the lines are getting mitered instead of beveled, which should be a simple enough thing to fix (see https://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-miter-limit for reference).

@Lightup1
Copy link
Author

fig.pdf
fig.pdf
fig_highers.png
fig_highres

@asinghvi17
Copy link
Member

asinghvi17 commented Apr 27, 2022

Here is an MWE:

using CairoMakie
ts = LinRange(1e-10, 1, 200)
fig, ax, plt = lines(ts, sin.(1 ./ ts); linewidth = 10)
ylims!(-10,10)
fig

iTerm2 I2Xdyd
Note that when I increase the density of the x-vector by a factor of 10, this is somewhat ameliorated:
iTerm2 ZVyq0f

Here is a quick fix I thought of. If you run the code below in the REPL, you should see this:
iTerm2 HLiXrY

Click here to see code
@eval CairoMakie begin
"""
    CairoScreen(
        scene::Scene, path::Union{String, IO}, mode::Symbol;
        antialias = Cairo.ANTIALIAS_BEST
    )
Creates a CairoScreen pointing to a given output path, with some rendering type defined by `mode`.
"""
function CairoScreen(scene::Scene, path::Union{String, IO}, mode::Symbol; device_scaling_factor = 1, antialias = Cairo.ANTIALIAS_BEST)

    # the surface size is the scene size scaled by the device scaling factor
    w, h = round.(Int, scene.camera.resolution[] .* device_scaling_factor)

    if mode == :svg
        surf = Cairo.CairoSVGSurface(path, w, h)
    elseif mode == :pdf
        surf = Cairo.CairoPDFSurface(path, w, h)
    elseif mode == :eps
        surf = Cairo.CairoEPSSurface(path, w, h)
    elseif mode == :png
        surf = Cairo.CairoARGBSurface(w, h)
    else
        error("No available Cairo surface for mode $mode")
    end

    # this sets a scaling factor on the lowest level that is "hidden" so its even
    # enabled when the drawing space is reset for strokes
    # that means it can be used to increase or decrease the image resolution
    ccall((:cairo_surface_set_device_scale, Cairo.libcairo), Cvoid, (Ptr{Nothing}, Cdouble, Cdouble),
        surf.ptr, device_scaling_factor, device_scaling_factor)

    ctx = Cairo.CairoContext(surf)
    
    @ccall Cairo.libcairo.set_miter_limit(ctx.ptr::Ptr{Nothing}, 2.0::Cdouble)::Cvoid
    #ccall((:cairo_set_miter_limit, Cairo.libcairo), Cvoid, (Ptr{Nothing}, Cdouble), (ctx.ptr, 2.0))

    #Cairo.set_line_join(ctx, Cairo.CAIRO_LINE_JOIN_ROUND)

    Cairo.set_antialias(ctx, antialias)

    return CairoScreen(scene, surf, ctx, nothing)
end
"""
    CairoScreen(scene::Scene; antialias = Cairo.ANTIALIAS_BEST)
Create a CairoScreen backed by an image surface.
"""
function CairoScreen(scene::Scene; device_scaling_factor = 1, antialias = Cairo.ANTIALIAS_BEST)
    w, h = round.(Int, scene.camera.resolution[] .* device_scaling_factor)
    surf = Cairo.CairoARGBSurface(w, h)
    # this sets a scaling factor on the lowest level that is "hidden" so its even
    # enabled when the drawing space is reset for strokes
    # that means it can be used to increase or decrease the image resolution
    ccall((:cairo_surface_set_device_scale, Cairo.libcairo), Cvoid, (Ptr{Nothing}, Cdouble, Cdouble),
        surf.ptr, device_scaling_factor, device_scaling_factor)

    ctx = Cairo.CairoContext(surf)
    Cairo.set_antialias(ctx, antialias)
    # Set the miter limit (when miter transitions to bezel) to mimic GLMakie behaviour
    @ccall Cairo.libcairo.cairo_set_miter_limit(ctx.ptr::Ptr{Nothing}, 2.0::Cdouble)::Cvoid

    return CairoScreen(scene, surf, ctx, nothing)
end
end

@Lightup1
Copy link
Author

Dear @asinghvi17 , when I try to save the error occurs.
image

@Lightup1
Copy link
Author

The VS code plot plane works good.
image
But when you try to save
save("PotentialDeform.pdf",fig)
image
image

@asinghvi17
Copy link
Member

Sorry, typo - it should be Cairo.libcairo.cairo_set_miter_limit. Here is the new code:

Click here to see code
@eval CairoMakie begin
"""
    CairoScreen(
        scene::Scene, path::Union{String, IO}, mode::Symbol;
        antialias = Cairo.ANTIALIAS_BEST
    )
Creates a CairoScreen pointing to a given output path, with some rendering type defined by `mode`.
"""
function CairoScreen(scene::Scene, path::Union{String, IO}, mode::Symbol; device_scaling_factor = 1, antialias = Cairo.ANTIALIAS_BEST)

    # the surface size is the scene size scaled by the device scaling factor
    w, h = round.(Int, scene.camera.resolution[] .* device_scaling_factor)

    if mode == :svg
        surf = Cairo.CairoSVGSurface(path, w, h)
    elseif mode == :pdf
        surf = Cairo.CairoPDFSurface(path, w, h)
    elseif mode == :eps
        surf = Cairo.CairoEPSSurface(path, w, h)
    elseif mode == :png
        surf = Cairo.CairoARGBSurface(w, h)
    else
        error("No available Cairo surface for mode $mode")
    end

    # this sets a scaling factor on the lowest level that is "hidden" so its even
    # enabled when the drawing space is reset for strokes
    # that means it can be used to increase or decrease the image resolution
    ccall((:cairo_surface_set_device_scale, Cairo.libcairo), Cvoid, (Ptr{Nothing}, Cdouble, Cdouble),
        surf.ptr, device_scaling_factor, device_scaling_factor)

    ctx = Cairo.CairoContext(surf)
    
    @ccall Cairo.libcairo.cairo_set_miter_limit(ctx.ptr::Ptr{Nothing}, 2.0::Cdouble)::Cvoid
    #ccall((:cairo_set_miter_limit, Cairo.libcairo), Cvoid, (Ptr{Nothing}, Cdouble), (ctx.ptr, 2.0))

    #Cairo.set_line_join(ctx, Cairo.CAIRO_LINE_JOIN_ROUND)

    Cairo.set_antialias(ctx, antialias)

    return CairoScreen(scene, surf, ctx, nothing)
end
"""
    CairoScreen(scene::Scene; antialias = Cairo.ANTIALIAS_BEST)
Create a CairoScreen backed by an image surface.
"""
function CairoScreen(scene::Scene; device_scaling_factor = 1, antialias = Cairo.ANTIALIAS_BEST)
    w, h = round.(Int, scene.camera.resolution[] .* device_scaling_factor)
    surf = Cairo.CairoARGBSurface(w, h)
    # this sets a scaling factor on the lowest level that is "hidden" so its even
    # enabled when the drawing space is reset for strokes
    # that means it can be used to increase or decrease the image resolution
    ccall((:cairo_surface_set_device_scale, Cairo.libcairo), Cvoid, (Ptr{Nothing}, Cdouble, Cdouble),
        surf.ptr, device_scaling_factor, device_scaling_factor)

    ctx = Cairo.CairoContext(surf)
    Cairo.set_antialias(ctx, antialias)
    # Set the miter limit (when miter transitions to bezel) to mimic GLMakie behaviour
    @ccall Cairo.libcairo.cairo_set_miter_limit(ctx.ptr::Ptr{Nothing}, 2.0::Cdouble)::Cvoid

    return CairoScreen(scene, surf, ctx, nothing)
end
end

@Lightup1
Copy link
Author

Lightup1 commented Apr 28, 2022

Thanks, it works now
PotentialDeform.pdf
.

@asinghvi17
Copy link
Member

Glad to hear! Once #1844 is merged, this issue should close automatically. The fix will likely be out in the next release.

SimonDanisch added a commit that referenced this issue May 3, 2022
* Set the Cairo miter limit to mimic GLMakie

Fixes #1843

* Update NEWS.md

* Switch back from macro ccall to function ccall

Co-authored-by: Simon <sdanisch@protonmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CairoMakie This relates to CairoMakie, the vector backend for Makie based on Cairo.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants