GLMakie Camera Controls vs Scene Controls

I am trying to do a very simple thing - Create a Scene in GLMakie and then control it’s camera.

using GLMakie: Scene, scatter!, Circle, display, text!

s1 = Scene()
scatter!(s1, (0, 0), marker = Circle, markersize = 200, color = :red)
# text!(s1, (0, 0), text="(0,0)", color=:black, fontsize=12, align=(:center, :center))
scatter!(s1, (1, 0), marker = Circle, markersize = 200, color = :blue)
# text!(s1, (1, 0), text="(1,0)", color=:black, fontsize=12, align=(:center, :center))
scatter!(s1, (-1, 0), marker = Circle, markersize = 200, color = :green)
# text!(s1, (-1, 0), text="(-1,0)", color=:black, fontsize=12, align=(:center, :center))

display(s1)

# Creating a standard number plane for visualization
using GLMakie: lines!, text!

# Draw a line at each  0.5 units in the x and y direction and at each point on the line intersection, write a text with the coordinate of point with respect to the origin, the default x and y range is (-10, 10) for both axes

function draw_number_plane(scene; x_range=[-10, 10], y_range=[-10, 10], step=0.5)
    for x in x_range[1]:step:x_range[2]
        lines!(scene, [x, x], y_range, color=:gray, linewidth=0.5) # vertical lines
    end
    for y in y_range[1]:step:y_range[2]
        lines!(scene, x_range, [y, y], color=:gray, linewidth=0.5) # horizontal lines
    end

    # Write the text for each intersection 
    for x in x_range[1]:step:x_range[2]
        for y in y_range[1]:step:y_range[2]
            text!(scene, (x, y), text="($x, $y)", color=:black, fontsize=8, align=(:center, :center))
        end
    end
end

draw_number_plane(s1, x_range=[-10, 10], y_range=[-10, 10], step=0.5)

This creates a very nice grid in a Makie Scene

(For some reason, Axis(s1, …) didn’t work, so this was the method I came up with)

I then try

using GLMakie: translate!
# Translate the scene by (1, 1)
translate!(s1, (1, 1))
text!(s1, (-1, -1), text="(-1,-1) in transformed scene", color=:black, fontsize=12, align=(:center, :center))
s1.transformation
#https://docs.makie.org/v0.22/reference/generic/transformations heres more on this

Amazing! That is exactly what I intended. I assume this shifts everything in the Scene by (1,1) unit in Normalized Device Coordinates (the default coordinate system in OpenGL and hence GLMakie where the screen spans -1 to 1 are the x and y direction limits)

using GLMakie: rotate!, qrotation, Vec3f

# Equivalent Approach of using Quaternions
# qr = qrotation(Vec3f(0, 0, 1), pi / 4)
# rotate!(s1, qr) # Rotate the scene by 45 degrees (π/4 radians)

rotate!(s1, pi / 4) # Alternatively, you can use radians directly (Internally uses Quaternions)

Instead of rotating everything nicely by 45 degrees, it sort of applies a weighted rotation to everything, squishing everything.

This makes me wonder that these transformations are not those of the camera but the screen space coordinates of the scene (and hence all objects rendered in it) itself.

rotate!(s1, pi / 2)

I wonder why rotation by 90 degrees doesn’t squish things (well maybe it does since the distance between each point in x is still more than that in y)


I thus tried using rotate_cam! instead, as I realized that is what would actually be used to not disturb anything within the scene, but just move the camera around within the scene.

rotate_cam!(s1, Vec3f(0,0,45))

But this gives an error

MethodError: no method matching rotate_cam!(::Scene, ::Makie.EmptyCamera, ::Tuple{Float64})

Closest candidates are:
  rotate_cam!(::Scene, !Matched::Makie.OldCamera3D, ::Union{Tuple{Vararg{T, N}}, StaticArraysCore.StaticArray{Tuple{N}, T, 1}} where {N, T}, !Matched::Bool)
   @ Makie C:\Users\DELL\.julia\packages\Makie\ux0Te\src\camera\old_camera3d.jl:308
  rotate_cam!(::Scene, !Matched::Makie.OldCamera3D, ::Union{Tuple{Vararg{T, N}}, StaticArraysCore.StaticArray{Tuple{N}, T, 1}} where {N, T})
   @ Makie C:\Users\DELL\.julia\packages\Makie\ux0Te\src\camera\old_camera3d.jl:308
  rotate_cam!(::Any, !Matched::Makie.Camera3D, ::Union{Tuple{Vararg{T, N}}, StaticArraysCore.StaticArray{Tuple{N}, T, 1}} where {N, T}, !Matched::Any)
   @ Makie C:\Users\DELL\.julia\packages\Makie\ux0Te\src\camera\camera3d.jl:526
  ...


Stacktrace:
 [1] rotate_cam!(scene::Scene, theta_v::Float64)
   @ Makie C:\Users\DELL\.julia\packages\Makie\ux0Te\src\camera\old_camera3d.jl:306
 [2] top-level scope
   @ e:\Programming\Canvas\jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_X60sZmlsZQ==.jl:10

I don’t know why it didn’t work, even though the documentation uses it in a similar way (There isn’t explicitly an example under Cameras section).

Here’s an answer I found. My usual strategy while using Scenes is that things that work on figures usually work on scenes too, but generally by passing scene as the first parameter.
It seemingly doesn’t work for this.

I tried a bunch of different variations of using the function, but it didn’t work.


How do I manipulate the camera in a Scene? Any good suggestions, notes, or examples of moving the camera around (position as well as eye position if those are any different; rotations) vs. transforming the scene around?

Also, any nice explanations of the Spaces? Space vs. Markerspace in 2D vs. 3D and what does either do?

Thanks!

Answering my own question for people watching this in the future…

cam3d!(s1)
camc = cameracontrols(s1)
update_cam!(s1, camc, Vec3f(0.0, 0.0, 5.0), Vec3f(0.0,0.0,0.0), Vec3f(0.0,1.0,0.0)) 
# Sets camera position, lookat and upvector
rotate_cam!(s1, camc, Vec3f(pi/3, 0, 0)) # pi/3 radians around the x axis

The documentation doesn’t explicitly state it but from my testing, you need to have a camera controls object passed in as well.

Also, the rotate_cam! function rotates the camera in body-fixed composition of Euler Angles… XY’Z’'.

You might want to look at

# Rotation Center
camc.settings.rotation_center[] = :eyeposition # default is :lookat 
# :lookat preserves the lookat point and rotates the camera around it, but :eyeposition rotates the camera around the eyeposition, preserving the eyeposition instead. Upto you what to preserve.
camc.settings.rotation_center[] = :lookat # default is :lookat

Camera Controls (camc) in this case has loads of attributes to play around with!
You can change the fields manually but need to call update_cam!(scene) function to reflect the changes, which from what I understood updates the camera fields like view, etc based on fields like camera position (aka eyeposition), lookat and upvector.

4 Likes