Makie: align y axes at the origin (y=0) for different y ranges

I’m trying to create a single line graph plotting totally different multiple quantities with more than two y axes. I’m almost successful. See the plot below. One thing that I don’t know how to achieve is to align the y axes at y = 0. That is quite necessary to me.

I know I can specify the limits for each variable in such a way that y=0 comes at the same place, but that would be too tedious a program to write. . . .

using CairoMakie

xax = 0:(pi/16):pi
co = cos.(xax)
si = sin.(xax) .* 100
ta = tan.(xax) .* 0.001

lims = ((0,3), nothing)
col1 = Makie.wong_colors()[1]
col2 = :gray
col3 = Makie.wong_colors()[2]

fig = Figure()
ax1 = Axis(fig[1,2], limits=lims)
ax2 = Axis(fig[1,2], limits=lims, ylabel="var 2")
ax3 = Axis(fig[1,2], limits=lims)

lines!(ax1, xax, co, color=col1)
lines!(ax2, xax, si, color=col2)
lines!(ax3, xax, ta, color=col3)

linkxaxes!(ax1,ax2,ax3)
hidespines!(ax1, :r, :l, :b, :t)
hidespines!(ax3, :r, :l, :b, :t)
hidedecorations!(ax1)
hidedecorations!(ax3)

ax1_2 = Axis(fig[1,1], ylabel="var 1",
    yticklabelcolor = col1,
    leftspinecolor = col1,
    ytickcolor = col1,
    ylabelcolor = col1,
)
ax3_2 = Axis(fig[1,3], ylabel="var 3",
    yticklabelcolor = col3,
    leftspinecolor = col3,
    ytickcolor = col3,
    ylabelcolor = col3,
)

linkyaxes!(ax1, ax1_2)
hidexdecorations!(ax1_2)
hideydecorations!(ax1_2, label=false, ticklabels=false, ticks=false)
hidespines!(ax1_2, :r, :b, :t)

linkyaxes!(ax3, ax3_2)
hidexdecorations!(ax3_2)
hideydecorations!(ax3_2, label=false, ticklabels=false, ticks=false)
hidespines!(ax3_2, :r, :b, :t)

colsize!(fig.layout, 1, 15)
colsize!(fig.layout, 3, 15)
save("tmp.png", fig)

I think there’s no way around that? :slight_smile:

You added a question mark and yours is exactly my question, right? :stuck_out_tongue_winking_eye:

Yeah I mean there’s no exact solution for this, I think. You’d have to pick one configuration in which all zeros are in the same place but still all the data is in view. You can get the current limits with ax.finallimits[]

Thanks! I wanted to hear something like that.

Okay, I have to set the limits manually, but I’d like to utilize the “machinery” that Makie already has, as much as possible.

I’ve found another thread in this forum (see the link at the bottom) and am exploring the idea:

using CairoMakie
xs = 0:(pi/16):pi
co = cos.(xs)
fig = Figure()
ax = Axis(fig[1,1])
lines!(ax, xs, co)
rect = ax.finallimits[]
yorg = rect.origin[2]
ywid = rect.widths[2]
ytop =  # use yorg and ywid 
ybot = # use yorg and ywid
ylims!(ax, ybot, ytop)

But, what are these values? rect.widths[2] turns out to be 10 . . .


Ah that’s just because the limit calculation is only called before first display as an optimization. Before, we had it reset at every plot, but that kind of just resulted in lots of unnecessary updates. You can call reset_limits!(ax) to force it.

1 Like

So, following @jules 's advice, I’ve created a function to shift the origin of the y axis to a fixed place:

"""
Shift the origin of the y axis to vy0, which is a relative
position within the y axis such that 0 <= vy0 <= 1.
Positive y values will be fully included but negative values
may fall below the lower limit of the axis.
"""
function adjust_ylims!(ax, vy0)
  @assert 0 <= vy0 <= 1
  vdy2 = 1 - vy0 # above vy0
  vdy1 = vy0 # below vy0
  reset_limits!(ax)
  rect = ax.finallimits[]
  yorg = rect.origin[2]
  ywid = rect.widths[2]
  ytop = yorg + ywid
  @assert ytop > 0
  ybot = - ytop * (vdy1/vdy2)
  # ytop - ybot = ytop + ytop * (vdy1/vdy2)
  # = ytop * (1 + vdy1/vdy2) = ytop / vdy2
#  @show ybot, ytop
  ylims!(ax, ybot, ytop)
end

This isn’t general enough but for my present purpose at hand, it works.