Is it possible to achieve zooming in a portion of an (already drawn) axis and display it as another axis, or as an inset?
Something like this in matplotlib:
Is it possible to achieve zooming in a portion of an (already drawn) axis and display it as another axis, or as an inset?
Something like this in matplotlib:
Yup this is possible, specifically using the BBox function (which specifies the bounding box of the Axis
). Here’s a small example:
using CairoMakie
CairoMakie.activate!(type = "svg")
x = 0.0 : 0.1 : 10.0
y = exp.(-x)
begin
fig = Figure(size=(800,800))
ax1 = Axis(
fig[1,1];
xgridvisible=false, ygridvisible=false
)
lines!(ax1, x, y)
bracket!(5, 0.01, 10, 0.01; offset=5, style=:square, orientation=:up)
ax2 = Axis(
fig;
yscale=log10,
bbox=BBox(400, 750, 200, 600)
)
lines!(ax2, x[50:end], y[50:end])
fig
end
This produces:
Hope this helps!
This here worked on GLMakie v0.8.11
(includes update of rectangle-zoom for interactivity). The idea is to turn this into something like an insetaxis
method, but I haven’t found time for that yet.
using GLMakie
import GLMakie.Makie.GeometryBasics: coordinates
GLMakie.activate!()
x = collect(1:100)
y = randn(length(x))
fig = Figure()
ax = Axis(fig[1,1])
inset = Axis(fig[1,1]; width=Relative(0.8), height=Relative(0.2),
halign=0.9, valign=0.1, backgroundcolor=(:white,1.0))
scatterlines!(ax, x, y)
subx, suby = x[30:40], y[30:40]
scatterlines!(inset, subx, suby)
poly!(ax, inset.finallimits, color=(:black,0), strokewidth=1, strokecolor=:black)
translate!(inset.blockscene, 0, 0, 1000)
shifted_bbox = lift(ax.scene.px_area, inset.layoutobservables.computedbbox,
ax.scene.camera.projectionview) do px_area, bbox, _
Rect2f(bbox.origin .- px_area.origin, bbox.widths)
end
scatter!(ax, shifted_bbox, color=:red, space=:pixel)
vert_edges = [ Observable{Any}() for _ = 1:4 ]
onany(inset.finallimits, shifted_bbox) do bbox1, bbox2
for (i,(c1, c2)) in enumerate(zip(coordinates(bbox1), coordinates(bbox2)))
cc1 = Makie.project(ax.scene, Point2f(c1))
vert_edges[i][] = [ cc1, Point2f(c2) ]
end
end
notify(inset.finallimits)
for vs in vert_edges
lines!(ax, vs, color=:grey, space=:pixel)
end
display(fig)
I don’t think this is what I need, because this example actually draws the data twice (which is what I am doing right now). It’s easy enough for a simple line plot, but if I have multiple layers, or even dynamically drawn axis, I would prefer to just use the data already in axis one.
This also draws the data twice, so not exactly what I am looking for.
I had been looking for something like this for a while; essentially the equivalent of matplotlib’s indicate_inset_zoom
. I wrote it into a function which also selectively draws the lines which don’t cross the rectangle (similar to what matplotlib does by default). I’m posting here, because there might be others looking for something like this.
function draw_inset!(ax, inset)
poly!(ax, inset.finallimits, color=(:black,0), strokewidth=1, strokecolor=:black)
shifted_bbox = lift(ax.scene.viewport, inset.layoutobservables.computedbbox,
ax.scene.camera.projectionview) do px_area, bbox, _
Rect2f(bbox.origin .- px_area.origin, bbox.widths)
end
# scatter!(ax, shifted_bbox, color=:red, space=:pixel)
vert_edges = [ Observable{Any}() for _ in 1:4 ]
connect = [ Observable{Bool}() for _ in 1:4 ]
onany(inset.finallimits, shifted_bbox) do bbox1, bbox2
for (i,(c1, c2)) in enumerate(zip(Makie.GeometryBasics.coordinates(bbox1), Makie.GeometryBasics.coordinates(bbox2)))
cc1 = Makie.project(ax.scene, Point2f(c1))
vert_edges[i][] = [ cc1, Point2f(c2) ]
end
rectlims = vcat(Makie.project(ax.scene, Point2f(inset.finallimits[].origin))...,
Makie.project(ax.scene, Point2f(inset.finallimits[].origin.+inset.finallimits[].widths))...) # x0 y0 x1 y1
x0 = shifted_bbox[].origin[1] > rectlims[1]
x1 = shifted_bbox[].origin[1] + shifted_bbox[].widths[1] > rectlims[3]
y0 = shifted_bbox[].origin[2] > rectlims[2]
y1 = shifted_bbox[].origin[2] + shifted_bbox[].widths[2] > rectlims[4]
connect[1][] = xor(x0, y0)
connect[2][] = x1==y0
connect[3][] = x0==y1
connect[4][] = xor(x1, y1)
end
notify(inset.finallimits)
for (c, vs) in zip(connect,vert_edges)
lines!(ax, vs, color=:grey, space=:pixel, visible=c)
end
end
The result looks like this:
The only bug I can’t be bothered to fix right now is that if no xlims or ylims are set, it will set them to (0,10). Calling xlims!()
or ylims!()
with empty argument fixes it though. Thanks @fatteneder !
For this kind of functionality, see zoom_lines!(ax, inset)
in MakieExtra.jl: