CairoMakie: any way to save 3D plot in vector format (.svg, .eps)?

Hi, so far I’ve used CairoMakie for 2D plots (exporting to .svg) while using GLMakie for 3D plots (for interactivity & exporting to .png). Is there any way to use CairoMakie with a 3D plot and export it to .svg? Speed doesn’t matter here, but it needs to be publication-quality and with editable axes.

When I do this for simple examples (Axis3 & lines!() with CairoMakie), it produces a bitmap in the .svg. In more complex cases, it fails upon display or .save() due to colormap issues:

MethodError: no method matching interpolated_getindex(::Vector{ColorTypes.RGBA{Float32}}, ::Float64, ::Vector{Float64})
Closest candidates are:
interpolated_getindex(::AbstractArray{T}, ::AbstractFloat) where T at ~/.julia/packages/Makie/lgPZh/src/colorsampler.jl:45
interpolated_getindex(::AbstractArray, ::Number, ::Union{Tuple{Vararg{T, N}}, StaticArrays.StaticVector{N, T}} where {N, T}) at ~/.julia/packages/Makie/lgPZh/src/colorsampler.jl:34

(here I used color = one of the vertex coordinates, colormap = :rainbow, with a specific color range)

Is there any hope to do this in CairoMakie? Or to add the feature of saving to .svg in GLMakie (my favorite)? I know this would be a lot of work, so a simple “no” is of course acceptable…

Thanks!

When a surface is drawn with mesh primitives, Cairo makes the svg into a bitmap silently, as you noticed. That’s because the Coons mesh operator is not in the svg spec.

You could try setting the rasterize keyword on the mesh/surface, that has been a recent addition to CairoMakie. It should force one normal bitmap for the surface only, not the whole svg.

Then I recently came up with a workaround to get transparent color buffers from GLMakie, and @asinghvi17 has tried to combine such glmakie renders with CairoMakie figures. But I don’t know how that has developed.

Thanks, I think that would help! I didn’t find how to use the rasterize keyword however, is this somewhere in scene? Or during plotting? At least for lines!(…), I don’t see an effect.

or to be more specific: how would I set the line here to be rasterized, with the axis rendered in vector format?

fig = Figure(resolution=(400,400)); ax = Axis3(fig[1,1]); 
t=0:.01:1; x=t; y=sin.(t*2π); z=cos.(t*2π); 
p = lines!(ax, x,y,z, color=x, colormap=:rainbow, lw=5);

I think just rasterize = true or rasterize = 5 or whatever multiplier in the lines call

Are you on the latest version? It’s pretty recent

hm, just updated via “up” in the package manager; Makie = 0.17.7, CairoMakie = 0.8.7, GLMakie = 0.6.7

But I’m still on julia 1.7.2, so that could be a problem. I’ll try updating to 1.7.3 later.

That looks like latest I think, hm I haven’t used rasterize myself yet, but I think that’s how it should work

Yeah, that’s how I designed the rasterization interface - just as a per-plot parameter. On the Cairo-GLMakie integration front, that basically works now. The only things left to do are figuring out how to deal with the multiple display bit, get displays sized correctly for GLMakie, and allowing the user to rasterize Scenes as whole objects (for e.g. depth buffering). The API in that PR would be rasterize = (GLMakie, scale::Real).

If you can work with PDF that might be a better option to consider, since it supports mesh gradients directly.

That would be great, I think most people (me included) would be very happy to have a PDF (or EPS) export from Makie. Go for the easiest route first - right now it’s a bit tedious to rewrite everything for another plot library to get publication-ready output.

Actually now that I looked at it again, you were talking about Axis3 with a line plot, right? So the only meshes in there are the three panels. You should be able to just hide those with xpanelvisible = false, ypanelvisible = false, zpanelvisible = false and then the basic pdf/svg save without bitmap fallback should work.

As @jules said, PDF and EPS export already exist, you can call that out of the box with CairoMakie. Looks like that mesh is an issue for SVG, but I think it shouldn’t cause PDF to rasterize.

Just to clarify, what do you mean by editable axes? If it’s just the text then yes, this should work. If you want to modify the tick lines or something, you could mess with that plot in Makie itself (have a look at ax.blockscene and ax.blockscene.plots where ax is your Axis3).

Thank you all, hiding the panels finally did it! I couldn’t try it on the real figure before because of an unrelated issue: setting colorrange with an array works with GLMakie, but not with CairoMakie. Minimal example:

fig = Figure();
ax = Axis3(fig[1,1], xypanelvisible=false, yzpanelvisible=false, xzpanelvisible=false);
t=0:.01:1; x=t; y=sin.(t*2π); z=cos.(t*2π);
lines!(ax, x,y,z, color=t, colormap=:rainbow, colorrange = [0,.8], lw=5);
fig

gives the error from the first post with CairoMakie and can be solved by using a Tuple: colorrange = (0,.8)

After that, the export produces vector output in PDF and SVG, but not in EPS (still rasterized). For me, PDF is the better option, as it has text that can be selected/copied from a PDF viewer and modified by an external editor (if other people want to change an axis label, title, etc.).

So thanks again! Maybe the Array / Tuple thing should be documented somewhere or corrected for CairoMakie; for me the error message was too cryptic and I found the solution by googling for a similar error.

edit: actually the error message makes perfect sense now, I should have read it more carefully.

edit2: you are right @asinghvi17, the PDF output works even without hiding the panels, so it’s a SVG-only problem.