# Makie: Linking cameras in multiple 3d plots

**URL:** https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309
**Category:** Visualization
**Tags:** makie, glmakie
**Created:** [June 24, 2022, 12:38pm UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309 "2022-06-24T12:38:38Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![Thomberger](https://avatars.discourse-cdn.com/v4/letter/t/258eb7/32.png) [@Thomberger](https://discourse.julialang.org/u/Thomberger)
#### Post date: [June 24, 2022, 12:38pm UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309/1 "2022-06-24T12:38:38Z")

</div>

Is there a way to link cameras in multiple 3d plot. For now I use my first axis as the main one, and update the azimuth and elevation all all the other axis based on this one. However, with this solution i can change the view only with the first axis. Is there a way to do that with any axis where all the axis are linked altogether ?

For now my solution looks like that :

```julia
using GLMakie
f = Figure()

ax1 = Axis3(f[1, 1],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax1,rand(20,3))

ax2 = Axis3(f[1, 2],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax2,rand(20,3))

on(ax1.attributes.azimuth) do x
    ax2.attributes.azimuth = x
end
on(ax1.attributes.elevation) do x
    ax2.attributes.elevation = x
end

# on(ax2.attributes.azimuth) do x
# ax1.attributes.azimuth = x
# end
# on(ax2.attributes.elevation) do x
# ax1.attributes.elevation = x
# end

```

Where the commented part is what i would like but doesn’t work as it create an updating loop and makes julia crash.

---

<div class="post-metadata">

### Author: ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)
#### Post date: [June 24, 2022, 12:55pm UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309/2 "2022-06-24T12:55:14Z")

</div>

You have to change the logic so the update only happens if the values differ, otherwise it’s cyclical.

---

<div class="post-metadata">

### Author: ![Thomberger](https://avatars.discourse-cdn.com/v4/letter/t/258eb7/32.png) [@Thomberger](https://discourse.julialang.org/u/Thomberger)
#### Post date: [June 24, 2022, 1:06pm UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309/3 "2022-06-24T13:06:41Z")

</div>

Yep that’s working ! Thanks for the idea i was overthinking this problem !

---

<div class="post-metadata">

### Author: ![Thomberger](https://avatars.discourse-cdn.com/v4/letter/t/258eb7/32.png) [@Thomberger](https://discourse.julialang.org/u/Thomberger)
#### Post date: [June 24, 2022, 1:22pm UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309/4 "2022-06-24T13:22:11Z")

</div>

For posterity this function will link all the views of Axis3 in a figure:

```julia
function link_cameras(f; step=0.01)
    axes = f.content[findall(x -> typeof(x) == Axis3,f.content)]

    for i in 1:length(axes)
        on(axes[i].attributes.azimuth) do x
            for j in collect(1:length(axes))[1:end .!= i]
                if abs(x - axes[j].attributes.azimuth[])>step
                    axes[j].attributes.azimuth = x
                end
            end
        end
        on(axes[i].attributes.elevation) do x
            for j in collect(1:length(axes))[1:end .!= i]
                if abs(x - axes[j].attributes.elevation[])>step
                    axes[j].attributes.elevation = x
                end
            end
        end
    end
    return f
end

```

And with a MWE:

```julia
using GLMakie
f = Figure()

ax1 = Axis3(f[1, 1],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax1,rand(20,3))

ax2 = Axis3(f[1, 2],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax2,rand(20,3))

ax3 = Axis3(f[2, 1],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax3,rand(20,3))

ax4 = Axis3(f[2, 2],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax4,rand(20,3))

f = link_cameras(f)

```

---

<div class="post-metadata">

### Author: ![Thomberger](https://avatars.discourse-cdn.com/v4/letter/t/258eb7/32.png) [@Thomberger](https://discourse.julialang.org/u/Thomberger)
#### Post date: [June 28, 2022, 11:09am UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309/6 "2022-06-28T11:09:43Z")

</div>

For posterity, here is two functions linking all views in a figure ! Functions work either with Axis3 or with Lscene.

In axis3 there is no zoom, so azimuth and elevation of all axis in a figure are linked with this function

```julia
function link_cameras_axis3(f; step=0.01)
    axes = f.content[findall(x -> typeof(x) == Axis3,f.content)]

    for i in 1:length(axes)
        on(axes[i].attributes.azimuth) do x
            for j in collect(1:length(axes))[1:end .!= i]
                if abs(x - axes[j].attributes.azimuth[])>step
                    axes[j].attributes.azimuth = x
                end
            end
        end
        on(axes[i].attributes.elevation) do x
            for j in collect(1:length(axes))[1:end .!= i]
                if abs(x - axes[j].attributes.elevation[])>step
                    axes[j].attributes.elevation = x
                end
            end
        end
    end
    return f
end

```

In Lscene there is a complete camera object, so when a eye position is changed, it will update all the parameters of the other LScenes of the figure with this function:

```julia
function link_cameras_lscene(f; step=0.01)
    scenes = f.content[findall(x -> typeof(x) == LScene,f.content)]
    cameras = [x.scene.camera_controls for x in scenes]

    for i in 1:length(cameras)
        on(cameras[i].eyeposition) do x
            for j in collect(1:length(cameras))[1:end .!= i]
                if sum(abs.(x - cameras[j].eyeposition[]))>step
                    cameras[j].lookat[] = cameras[i].lookat[]
                    cameras[j].eyeposition[] = cameras[i].eyeposition[]
                    cameras[j].upvector[] = cameras[i].upvector[]
                    cameras[j].zoom_mult[] = cameras[i].zoom_mult[]
        
                    update_cam!(scenes[j].scene, cameras[j])
                end
            end
        end
    end
    return f
end

```

Finally, here is a MWE:

```julia
using GLMakie
f = Figure()
ax1 = Axis3(f[1, 1],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax1,rand(20,3))
ax2 = Axis3(f[1, 2],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax2,rand(20,3))
ax3 = Axis3(f[2, 1],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax3,rand(20,3))
ax4 = Axis3(f[2, 2],aspect=:data, viewmode=:fitzoom)
meshscatter!(ax4,rand(20,3))
f = link_cameras_axis3(f)

f = Figure()
ax1 = LScene(f[1, 1],aspect=:data)
meshscatter!(ax1,rand(20,3))
ax2 = LScene(f[1, 2],aspect=:data)
meshscatter!(ax2,rand(20,3))
ax3 = LScene(f[2, 1],aspect=:data)
meshscatter!(ax3,rand(20,3))
ax4 = LScene(f[2, 2],aspect=:data)
meshscatter!(ax4,rand(20,3))
f = link_cameras_lscene(f)

```

---

<div class="post-metadata">

### Author: ![t-bltg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/t-bltg/32/25526_2.png) [@t-bltg](https://discourse.julialang.org/u/t-bltg)
#### Post date: [September 13, 2023, 8:10am UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309/7 "2023-09-13T08:10:27Z")

</div>

Here is an updated (and simplified) example working on latest `Makie`:

```julia
using GLMakie

link_cameras_axis3(f; step=.01) = begin
  axes = filter(x -> x isa Axis3, f.content)

  for i ∈ 1:length(axes)
    lift(axes[i].azimuth, axes[i].elevation) do az, el
      for j ∈ 1:length(axes)
        i == j && continue
        if abs(az - axes[j].azimuth[]) > step
          axes[j].azimuth = az
        end
        if abs(el - axes[j].elevation[]) > step
          axes[j].elevation = el
        end
      end
    end
  end
  f
end

link_cameras_lscene(f; step=.01) = begin
  scenes = filter(x -> x isa LScene, f.content)
  cameras = map(x -> cameracontrols(x.scene), scenes)

  for i ∈ 1:length(cameras)
    on(cameras[i].eyeposition) do eye
      for j ∈ 1:length(cameras)
        i == j && continue
        if sum(abs, eye - cameras[j].eyeposition[]) > step
          update_cam!(scenes[j].scene, cameras[i])
        end
      end
    end
  end
  f
end

test_axis3() = begin
  f = Figure()
  ax1 = Axis3(f[1, 1], aspect=:data, viewmode=:fitzoom)
  meshscatter!(ax1, rand(20, 3))
  ax2 = Axis3(f[1, 2], aspect=:data, viewmode=:fitzoom)
  meshscatter!(ax2, rand(20, 3))
  ax3 = Axis3(f[2, 1], aspect=:data, viewmode=:fitzoom)
  meshscatter!(ax3, rand(20, 3))
  ax4 = Axis3(f[2, 2], aspect=:data, viewmode=:fitzoom)
  meshscatter!(ax4, rand(20, 3))
  link_cameras_axis3(f)
end

test_lscene() = begin
  f = Figure()
  ax1 = LScene(f[1, 1], scenekw=(; aspect=:data))
  meshscatter!(ax1, rand(20, 3))
  ax2 = LScene(f[1, 2], scenekw=(; aspect=:data))
  meshscatter!(ax2, rand(20, 3))
  ax3 = LScene(f[2, 1], scenekw=(; aspect=:data))
  meshscatter!(ax3, rand(20, 3))
  ax4 = LScene(f[2, 2], scenekw=(; aspect=:data))
  meshscatter!(ax4, rand(20, 3))
  link_cameras_lscene(f)
end

main() = begin
  test_axis3() |> display
  sleep(1)
  test_lscene() |> display
  return
end

main()

```

---

<div class="post-metadata">

### Author: ![edljk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/edljk/32/2429_2.png) [@edljk](https://discourse.julialang.org/u/edljk)
#### Post date: [March 17, 2025, 3:02pm UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309/8 "2025-03-17T15:02:05Z")

</div>

Is it possible to adapt the linking to the new zoom feature of Axis3? I tried without success..  
Thanks!

---

<div class="post-metadata">

### Author: ![asinghvi17](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/asinghvi17/32/8272_2.png) [@asinghvi17](https://discourse.julialang.org/u/asinghvi17)
#### Post date: [March 17, 2025, 7:55pm UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309/9 "2025-03-17T19:55:53Z")

</div>

I believe you would just link `ax.finallimits`, `ax.aspect`, and `ax.elevation`…

---

<div class="post-metadata">

### Author: ![edljk](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/edljk/32/2429_2.png) [@edljk](https://discourse.julialang.org/u/edljk)
#### Post date: [March 17, 2025, 9:25pm UTC](https://discourse.julialang.org/t/makie-linking-cameras-in-multiple-3d-plots/83309/10 "2025-03-17T21:25:37Z")

</div>

Great, thank you so much! It works great:

```julia

function link_cameras_axis3(f; step = .01) 
    axes = filter(x -> x isa Axis3, f.content)
    for i ∈ eachindex(axes)
        lift(axes[i].azimuth, axes[i].elevation, axes[i].aspect,
             axes[i].finallimits) do az, el, as, lm
            for j ∈ eachindex(axes)
                i == j && continue
                if abs(az - axes[j].azimuth[]) > step
                    axes[j].azimuth = az
                end
                if abs(el - axes[j].elevation[]) > step
                    axes[j].elevation = el
                end
                if norm(lm.origin - axes[j].finallimits[].origin) > step ||  
                   norm(lm.widths - axes[j].finallimits[].widths) > step
                    axes[j].finallimits = lm
                end
            end
        end
    end
    f
end

```
