Makie - How to insert an Axis3 into a scene so that zooming on a plot is possible?

Hi!
I can make a basic scene that lets me pan and zoom the way I want to like so:

using GLMakie 
scene = Scene()
cam3d!(scene;
rotation_center=:lookat,
projectiontype = :Orthographic,
fixed_axis=:false,
zoom_shift_lookat=:true)
meshscatter!(scene, rand(Point3f, 10), color=:purple)
scene

And I can make a basic 3D axis to plot stuff onto like this:

using GLMakie 
fig = Figure()
Axis3(fig[1,1])
meshscatter!(rand(Point3f, 10), color=:purple)
fig

My problem is that, while I can rotate the Axis object, I cannot zoom in and out like I can with the Scene object.
Would you be so kind as to help me get zoom enabled on a Axis3 object?
Thanks for your time!

Axis3 has no zoom because the content would just stick out of the axis. You can use LScene which is the default for 3d plots anyway. For LScene the axis moves with the plot (which has other problems, but you can zoom)

1 Like

Thank you so much, Jules!
I’ve been going crazy trying to figure this out!
I’ll go try LScene right now.
Thanks again! :grin:

HAHAHAHA!!! :rofl:
You fixed it, Jules!
You’re a Genius!
Thank you, thank you, thank you!

I’m pooped for now and I need to charge my laptop and I need to clean up my code from all the experimenting.
But I will come back and post the code so you can see how you helped me.

As promised, here’s a sample of the code that you helped fix:

using GLMakie

# Converts 3-element vectors or 3-tuples into 3 vectors for calling Makie functions
function to_components(vectors::Vector{T}) where T <: Union{
	Vector{<:Integer},
	Vector{<:AbstractFloat},
	Tuple{<:Integer,<:Integer,<:Integer},
	Tuple{<:AbstractFloat,<:AbstractFloat,<:AbstractFloat}
}
	xs = Float64[]
	ys = Float64[]
	zs = Float64[]
	for ve in vectors
		push!(xs, ve[1])
		push!(ys, ve[2])
		push!(zs, ve[3])
	end
	xs, ys, zs
end

function draw_axis()
    fig = Figure()
    lscene = LScene(fig[1,1])
	# Generate crosshairs at origin
	ps = [-1,1]
	origin = vcat(
		[[x,0,0] for x in ps],
		[[0,y,0] for y in ps],
		[[0,0,z] for z in ps]
	)
	xs, ys, zs = to_components(origin)
	
	# Draw and display crosshairs
	linesegments!(xs, ys, zs, linewidth=5, color=:brown)
	fig
end

# Helper function; draws a vector outlined by a cube with highlighted verices

function vector_box(tail::T, tip::S; clr=:black) where {
	T <: Union{Vector{<:Integer},Vector{<:AbstractFloat}},
	S <: Union{Vector{<:Integer},Vector{<:AbstractFloat}}
}

	# Destructure the ends of the vector
	x, y, z = tail
	s, t, u = tip

	# Use the components of the endpoints of the vector to set up permutation vectors
	# so that the computer can perform the iterative process of generating the vertex
	# vectors and edge vectors
	trans_x = [x, s]
	trans_y = [y, t]
	trans_z = [z, u]

	# Generate a mulit-dimensional array containing points that define the vertices
	vertices = [[x, y, z] for x in trans_x, y in trans_y, z in trans_z]

	# Generate a multidimensional array containing the point pairs that define the edges
	edges_pre = vcat(
		[[[x,y,z],[s,y,z]] for y in trans_y, z in trans_z],
		[[[x,y,z],[x,t,z]] for x in trans_x, z in trans_z],
		[[[x,y,z],[x,y,u]] for x in trans_x, y in trans_y]
	)

	# Container for the vectorized (re-shaped) edges array
	edges_mid = Vector{Float64}[]

	# Destructure the edge pairs so they can be further destructured for plotting
	for edge in vec(edges_pre)
		t1, t2 = edge
		push!(edges_mid, t1)
		push!(edges_mid, t2)
	end
	
	# Finally get lists of edge components for plotting
	edges_x, edges_y, edges_z = to_components(edges_mid)

	# Plot the vector outlined with a cube
	xs, ys, zs = to_components(vec(vertices))
	meshscatter!(xs, ys, zs, color=clr, markersize=0.3)
	linesegments!(edges_x, edges_y, edges_z, linestyle=:dot, color=clr, linewidth=5)
	arrows!([x], [y], [z], [s], [t], [u], color=clr, linewidth=0.25)
end

# Helper function
# NOTE: This function requires a Figure object!
# use draw_axis3D or another means to initialize a Figure.
# Geometrically adds an arbitrary number of 3D vectors
# Vectors to be summed must be given as a list
# e.g., add([[1,2,3], [4,5,6]]) will return [5,7,9]
function add(vectors::Vector{T}) where T <: Union{
	Vector{<:Integer}, 
	Vector{<:AbstractFloat}
}
	# Draw the vectors to be summed
	prev = [0,0,0]
	for ve in vectors
	    clr = RGBf(rand(3)...)
		vector_box(prev, prev .+ ve; clr)
		prev = prev .+ ve
	end
	
	# Draw the summation vector labeled with the summed point
	vector_box([0,0,0], prev; clr=:springgreen2)
	text!("$(prev)", position = (prev[1],prev[2],prev[3]), color=:red)
end

# A convenience function that provides its own axis for ploting single operations.
# Use draw_axis3D() with separate vector functions for more complex operations.
# Geometrically adds an arbitrary number of 3D vectors
# Vectors to be summed must be given as a list
# e.g., draw_add([[1,2,3], [4,5,6]]) will return [5,7,9]
function draw_add(vectors::Vector{T}) where T <: Union{Vector{<:Integer}, Vector{<:AbstractFloat}
}
	# Establish axes, plot and dispay vectors
	draw_axis()
	
	add(vectors)
	current_figure()
end

For example, you could execute

draw_add([[-1,-2,-3],[-4,-5,-6],[7,8,9]])

Thanks again!

how does one change the axis labels when using an LScene as described above? if i switch to using Axis3 instead, i know how to specify labels, but then can’t zoom. i’d like to do both.

axis = ax.scene[OldAxis]
axis[:names, :axisnames] = ("x", "y", "z")

as in :

1 Like

thanks! and for those following along, you can change the limits like this:

a.scene[OldAxis][:ticks, :ranges] = ([1,10],[0,1],[0,1])

am i to infer from it’s name that OldAxis will soon be deprecated?