Update rotation before next op

I’m trying to rotate a camera object and frame all visible objects by setting it’s rotation_euler and using bpy.ops.view3d.view_all() but it using the cameras original matrix when framing instead of the updated matrix.

I’m having trouble understanding why bpy.ops.view3d.view_all() is not using the updated matrix, even after updating the view layer via bpy.context.view_layer.update()

If you run the following script in the default scene you will see that even though the camera matrix is updated, the call to bpy.ops.view3d.view_all() doesn’t use it.

Note: if you want to run the script multiple times, run it twice each time to get the desired output - the camera view is toggled rather than set

import bpy
from mathutils import Euler, Matrix
from math import radians


cam = bpy.context.scene.objects["Camera"]
override = bpy.context.copy()
area = bpy.context.area
if area.type != 'VIEW_3D':
    override["area"] = [area for area in bpy.context.screen.areas if area.type == 'VIEW_3D'][0]
    override["region"] = [region for region in override["area"].regions if region.type == 'WINDOW'][0]
override["area"].spaces[0].lock_camera = True
bpy.ops.view3d.view_camera(override)


#1. Check cameras initial matrix
print("1.\n")
print("cam.matrix_local initial\n", cam.matrix_local, "\n")

#2. Create a 4x4 rotation matrix and check its output
rotation_euler = Euler((radians(45), 0.0, -radians(45)), 'XYZ')
rot_mat = rotation_euler.to_matrix()
rot_mat.resize_4x4()
print("2.\n")
print("local/rotation matrix to assign to cam.matrix_local:", "\n", rot_mat, "\n")

#3. Orientate the camera using the rotation matrix
# - Check set value
# - Check that the dependency graph has been tagged for an update. This should be
#   true because from what I understand ID property updates automatically tag for 
#   updates
cam.matrix_local = rot_mat
print("3.\n")
print("Cam Flagged for Update:", bpy.context.view_layer.depsgraph.id_type_updated(id_type='OBJECT'))
print("cam.matrix_local BEFORE view layer update\n", cam.matrix_local, "\n")

#4. Update the view layer
# - Check that the dependency graph now hasn't got any flagged updates. 
#   This should be false
# - Check the value of cam.matrix.local to see if it has been updated
bpy.context.view_layer.update()
print("4.\n")
print("Cam Flagged for Update:", bpy.context.view_layer.depsgraph.id_type_updated(id_type='OBJECT'))
print("cam.matrix_local AFTER view layer update\n", cam.matrix_local, "\n")

#5. Frame all objects 
# - Check the cam.matrix_local to see if it's rotation still matches what was
#   set previously. 
#   !! THIS IS NOT MATCHING. IT HAS USED THE ORIGINAL VALUE OF cam.matrix_local
#   !! I EXPECTED IT TO USE THE UPDATED MATRIX ROTATION VALUES
bpy.ops.view3d.view_all(override)
print("5.\n")
print("cam.matrix_local AFTER framing all objects!!!\n", cam.matrix_local, "\n")
print("WHY ARE THE ORGINAL ROTATION VALUES BEING USED AND NOT THE UPDATED?")

Output

1.

cam.matrix_local initial
 <Matrix 4x4 ( 0.7071, 0.5000, -0.5000, 0.0000)
            (-0.7071, 0.5000, -0.5000, 0.0000)
            ( 0.0000, 0.7071,  0.7071, 0.0000)
            ( 0.0000, 0.0000,  0.0000, 1.0000)>

2.

local/rotation matrix to assign to cam.matrix_local:
 <Matrix 4x4 ( 0.7071, 0.5000, -0.5000, 0.0000)
            (-0.7071, 0.5000, -0.5000, 0.0000)
            (-0.0000, 0.7071,  0.7071, 0.0000)
            ( 0.0000, 0.0000,  0.0000, 1.0000)>

3.

Cam Flagged for Update: True
cam.matrix_local BEFORE view layer update
 <Matrix 4x4 ( 0.7071, 0.5000, -0.5000, 0.0000)
            (-0.7071, 0.5000, -0.5000, 0.0000)
            ( 0.0000, 0.7071,  0.7071, 0.0000)
            ( 0.0000, 0.0000,  0.0000, 1.0000)>

4.

Cam Flagged for Update: False
cam.matrix_local AFTER view layer update
 <Matrix 4x4 ( 0.7071, 0.5000, -0.5000, 0.0000)
            (-0.7071, 0.5000, -0.5000, 0.0000)
            ( 0.0000, 0.7071,  0.7071, 0.0000)
            ( 0.0000, 0.0000,  0.0000, 1.0000)>

5.

cam.matrix_local AFTER framing all objects!!!
 <Matrix 4x4 ( 0.8186, -0.2017,  0.5378,  12.1344)
            ( 0.5742,  0.3097, -0.7578, -13.7233)
            (-0.0137,  0.9292,  0.3693,   9.8845)
            ( 0.0000,  0.0000,  0.0000,   1.0000)>

WHY ARE THE ORGINAL ROTATION VALUES BEING USED AND NOT THE UPDATED?

After looking into the c code base for the view_all operator I can see that it uses the RegionView3D view data to calculate the offset. These values are derived from the camera when in camera view but when moving a camera programmatically the view data needs to be updated using the RegionView3D.update() method. Adding a call to this method before calling bpy.ops.view3d.view_all solved my problem.