Cameras
Generally speaking, the camera controls the range of coordinates that are shown and from what perspective they are shown. For example, a 2D camera will give us a 2D perspective, meaning one where x extends right, y extends up and z extend towards the viewer. The range of x and y values shown on the screen is also controlled by the camera.
Overview
The term "camera" in Makie can be quite confusing as there are multiple things controlling, processing and storing camera related objects.
Before Block
s like Axis
were introduced, scenes were the main building block users interacted with. Every Block still relies on them internally, and LScene
specifically is just a light wrapper around Scene
. Up until Makie 0.24 every scene contained two fields related to cameras - camera(scene)
and cameracontrols(scene)
. The camera(scene)::Camera
stores camera matrices like projectionview
. Over time it has grown to also include eyeposition
, view_direction
and upvector
, which describe the orientation and placement of the camera. The cameracontrols(scene) <: AbstractCamera
are, as the name implies, an object that controls the camera. They generate the matrices stored in camera(scene)
which means they are responsible for the range of visible coordinates and the perspective from which they are shown. They also process user input like mouse drags if that has an effect on the camera, and they may include settings.
Block
s have their own interaction system, which is also used to control the camera. This effectively replaces cameracontrols(scene)
. For example, if you check cameracontrols(ax.scene)
for Axis
and PolarAxis
you will find an EmptyCamera
. LScene
still uses cameracontrols(scene)
as it is just a wrapper around Scene
.
Makie 0.24 brought in another layer with the scenes ComputeGraph
in scene.compute
. Currently the compute graph is fed by camera(scene)
to (lazily) calculate all the projection matrices needed to resolve the space
and markerspace
attributes. It is effectively now the final output of the camera pipeline.
Camera Controls
The cameracontrols(scene)
control how plot data is shown in Scene
and LScene
. They determine 2D vs 3D projections, the range of shown coordinates, the perspective and how these things react to user interaction.
Currently, we offer the following camera controllers/constructors
campixel!
: A 2D camera using pixel coordinatescam_relative!
: A 2D camera using relative (0..1) coordinatescam2d!
: A 2D camera using dynamic coordinate rangesCamera3D
: A general, highly adjustable 3D cameracam3d!
: ACamera3D
with default settingscam3d_cad!
: ACamera3D
with CAD-like settings
To specify the camera controller you can set the camera
attribute in a Scene
.
Scene(..., camera = cam3d!)
LScene(..., scenekw = (camera = cam3d!, ))
You can replace and existing camera in a scene:
scene = Scene(...)
cam3d!(scene)
ax = LScene(...)
cam3d!(ax.scene)
Pixel Camera
The pixel camera (campixel!
) projects the scene in pixel space, i.e. each integer step in the displayed data will correspond to one pixel. There are no controls for this camera. The z clipping limits are set to (-10_000, 10_000)
.
Relative Camera
The relative camera (cam_relative!
) projects the scene into a 0..1 by 0..1 space. There are no controls for this camera. The z clipping limits are set to (-10_000, 10_000)
.
2D Camera
The 2D camera (cam2d!
) uses an orthographic projection with a fixed rotation and aspect ratio. You can set the following attributes via keyword arguments in cam2d!
or by accessing the camera struct cam = cameracontrols(scene)
:
zoomspeed = 0.10f0
sets the speed of mouse wheel zooms.zoombutton = nothing
sets an additional key that needs to be pressed in order to zoom. Defaults to no key.panbutton = Mouse.right
sets the mouse button that needs to be pressed to translate the view.selectionbutton = (Keyboard.space, Mouse.left)
sets a set of buttons that need to be pressed to perform rectangle zooms.
The z clipping limits are set to (-10_000, 10_000)
.
Warning
This camera is not used by Axis
. It is used, by default, for 2D LScene
s and Scene
s.
3D Camera
Camera3D
is a generalized 3D camera with a large number of options. cam3d!
and cam3d_cad!
are specialized versions. The former is the default camera for 3D scenes. The latter is a camera that tries to mimic CAD-style cameras.
Makie.Camera3D Type
Camera3D(scene[; kwargs...])
Sets up a 3D camera with mouse and keyboard controls.
The behavior of the camera can be adjusted via keyword arguments or the fields settings
and controls
.
Settings
Settings include anything that isn't a mouse or keyboard button.
projectiontype = Perspective
sets the type of the projection. Can beOrthographic
orPerspective
.rotation_center = :lookat
sets the default center for camera rotations. Currently allows:lookat
or:eyeposition
.fixed_axis = true
: If true panning uses the (world/plot) z-axis instead of the camera up direction.zoom_shift_lookat = true
: If true keeps the data under the cursor when zooming.cad = false
: If true rotates the view aroundlookat
when zooming off-center.clipping_mode = :adaptive
: Controls hownear
andfar
get processed. Options::static
passesnear
andfar
as is:adaptive
scalesnear
bynorm(eyeposition - lookat)
and passesfar
as is:view_relative
scalesnear
andfar
bynorm(eyeposition - lookat)
:bbox_relative
scalesnear
andfar
to the scene bounding box as passed to the camera withupdate_cam!(..., bbox)
. (More specificallyfar = 1
is scaled to the furthest point of a bounding sphere andnear
is generally overwritten to be the closest point.)
center = true
: Controls whether the camera placement gets reset when callingcenter!(scene)
, which is called when a new plot is added.keyboard_rotationspeed = 1.0
sets the speed of keyboard based rotations.keyboard_translationspeed = 0.5
sets the speed of keyboard based translations.keyboard_zoomspeed = 1.0
sets the speed of keyboard based zooms.mouse_rotationspeed = 1.0
sets the speed of mouse rotations.mouse_translationspeed = 0.5
sets the speed of mouse translations.mouse_zoomspeed = 1.0
sets the speed of mouse zooming (mousewheel).circular_rotation = (false, false, false)
enables circular rotations for (fixed x, fixed y, fixed z) rotation axis. (This means drawing a circle with your mouse around the center of the scene will result in a continuous rotation.)
Controls
Controls include any kind of hotkey setting.
up_key = Keyboard.r
sets the key for translations towards the top of the screen.down_key = Keyboard.f
sets the key for translations towards the bottom of the screen.left_key = Keyboard.a
sets the key for translations towards the left of the screen.right_key = Keyboard.d
sets the key for translations towards the right of the screen.forward_key = Keyboard.w
sets the key for translations into the screen.backward_key = Keyboard.s
sets the key for translations out of the screen.zoom_in_key = Keyboard.u
sets the key for zooming into the scene (translate eyeposition towards lookat).zoom_out_key = Keyboard.o
sets the key for zooming out of the scene (translate eyeposition away from lookat).increase_fov_key = Keyboard.b
sets the key for increasing the fov.decrease_fov_key = Keyboard.n
sets the key for decreasing the fov.pan_left_key = Keyboard.j
sets the key for rotations around the screens vertical axis.pan_right_key = Keyboard.l
sets the key for rotations around the screens vertical axis.tilt_up_key = Keyboard.i
sets the key for rotations around the screens horizontal axis.tilt_down_key = Keyboard.k
sets the key for rotations around the screens horizontal axis.roll_clockwise_key = Keyboard.e
sets the key for rotations of the screen.roll_counterclockwise_key = Keyboard.q
sets the key for rotations of the screen.fix_x_key = Keyboard.x
sets the key for fixing translations and rotations to the (world/plot) x-axis.fix_y_key = Keyboard.y
sets the key for fixing translations and rotations to the (world/plot) y-axis.fix_z_key = Keyboard.z
sets the key for fixing translations and rotations to the (world/plot) z-axis.reset = Keyboard.left_control & Mouse.left
sets the key for resetting the camera. This equivalent to callingcenter!(scene)
.reposition_button = Keyboard.left_alt & Mouse.left
sets the key for focusing the camera on a plot object.translation_button = Mouse.right
sets the mouse button for drag-translations. (up/down/left/right)scroll_mod = true
sets an additional modifier button for scroll-based zoom. (true being neutral)rotation_button = Mouse.left
sets the mouse button for drag-rotations. (pan, tilt)
Other kwargs
Some keyword arguments are used to initialize fields. These include
eyeposition = Vec3d(3)
: The position of the camera.lookat = Vec3d(0)
: The point the camera is focused on.upvector = Vec3d(0, 0, 1)
: The world direction corresponding to the up direction of the screen.fov = 45.0
is the field of view. This is irrelevant if the camera uses an orthographic projection.near = automatic
sets the position of the near clip plane. Anything between the camera and the near clip plane is hidden. Must be greater 0. Usage depends onclipping_mode
.far = automatic
sets the position of the far clip plane. Anything further away than the far clip plane is hidden. Usage depends onclipping_mode
. Defaults to1
forclipping_mode = :bbox_relative
,2
for:view_relative
or a value derived from limits for:static
.
Note that updating these observables in an active camera requires a call to update_cam(scene)
for them to be applied. For updating eyeposition
, lookat
and/or upvector update_cam!(scene, eyeposition, lookat, upvector = Vec3d(0,0,1))
is preferred.
The camera position and orientation can also be adjusted via the functions
translate_cam!(scene, v)
will translate the camera by the given vectorv
.rotate_cam!(scene, angles)
will rotate the camera around its axes with the corresponding angles. The first angle will rotate around the cameras "right" that is the screens horizontal axis, the second around the up vector/vertical axis orVec3d(0, 0, +-1)
iffixed_axis = true
, and the third will rotate around the view direction i.e. the axis out of the screen. The rotation respects the currentrotation_center
of the camera.zoom!(scene, zoom_step)
will change the zoom level of the scene without translating or rotating the scene.zoom_step
applies multiplicatively tocam.zoom_mult
which is used as a multiplier to the fov (perspective projection) or width and height (orthographic projection).
Warning
This camera is not used by Axis3
. It is used, by default, for 3D LScene
s and Scene
s.
Camera and Projections
Sometimes you may need to interact with camera matrices to project data into a different space. As of Makie 0.24 you can get all the relevant matrices for this from scene.compute
using the helper functions:
Makie.get_projectionview(scene, space)
Makie.get_projection(scene, space)
Makie.get_view(scene, space)
Makie.get_preprojection(scene, space, markerspace)
Makie.get_space_to_space_matrix(scene, input_space, output_space)
Example - Visualizing the camera's view box
using GLMakie
using GeometryBasics, LinearAlgebra
function frustum_snapshot(cam)
r = Rect3f(-1, -1, -1, 2, 2, 2)
rect_ps = Makie.convert_arguments(Lines, r)[1]
inv_pv = inv(cam.projectionview[])
return map(rect_ps) do p
p = inv_pv * to_ndim(Point4f, p, 1)
return p[Vec(1,2,3)] / p[4]
end
end
ex = Point3f(1,0,0)
ey = Point3f(0,1,0)
ez = Point3f(0,0,1)
fig = Figure()
# Set up Scene shown by a camera
scene = LScene(fig[1, 1])
cc = Makie.Camera3D(scene.scene, projectiontype = Makie.Perspective, center = false)
linesegments!(scene, Rect3f(Point3f(-1), Vec3f(2)), color = :black)
linesegments!(scene,
[-ex, ex, -ey, ey, -ez, ez],
color = [:red, :red, :green, :green, :blue, :blue]
)
center!(scene.scene)
cam = scene.scene.camera
eyeposition = cc.eyeposition
lookat = cc.lookat
frustum = map(pv -> frustum_snapshot(cam), cam.projectionview)
# Set up scene visualizing the cameras view
scene = LScene(fig[1, 2])
_cc = Makie.Camera3D(scene.scene, projectiontype = Makie.Orthographic, center = false)
lines!(scene, frustum, color = :blue, linestyle = :dot)
scatter!(scene, eyeposition, color = :black)
scatter!(scene, lookat, color = :black)
linesegments!(scene,
[-ex, ex, -ey, ey, -ez, ez],
color = [:red, :red, :green, :green, :blue, :blue]
)
linesegments!(scene, Rect3f(Point3f(-1), Vec3f(2)), color = :black)
# Tweak initial camera position
update_cam!(scene.scene, Vec3f(4.5, 2.5, 3.5), Vec3f(0))
update_cam!(scene.scene, Vec3f(6, 8, 5), Vec3f(0))
fig

General Remarks
Buttons passed to the 2D and 3D camera are forwarded to ispressed
. As such you can pass false
to disable an interaction, true
to ignore a modifier, any button, collection of buttons or even logical expressions of buttons. See the events documentation for more details.