Viewport update changes world matrix. Why?

The following script assigns a world matrix to an object and its child, but after the viewlayer is updated the child’s world matrix is different, and I cannot understand why.

import bpy
from mathutils import Matrix

data = {
    "Cube" : {
        "verts" : [[-0.5, -0.5, 0], [0.5, -0.5, 0], [-0.5, -0.5, 1], [0.5, -0.5, 1], [0.5, 0.5, 0], [-0.5, 0.5, 0], [0.5, 0.5, 1], [-0.5, 0.5, 1]],
        "faces" : [[0, 1, 3, 2], [4, 5, 7, 6], [5, 0, 2, 7], [1, 4, 6, 3], [2, 3, 6, 7], [5, 4, 1, 0]],
        "matrix" : [
            (0.1877,  0.0000, 0.0000, 0.0000),
            (0.0000,  0.1170, 0.0000, 0.0000),
            (0.0000, -0.0000, 0.2957, 0.5130),
            (0.0000,  0.0000, 0.0000, 1.0000)]
    },
    "Cylinder" : {
        "verts" : [[0, 0, 0], [0, 0, 1], [0, -0.5, 0], [0, -0.5, 1], [0.25, -0.433, 0], [0.25, -0.433, 1], [0.433, -0.25, 0], [0.433, -0.25, 1], [0.5, 0, 0], [0.5, 0, 1], [0.433, 0.25, 0], [0.433, 0.25, 1], [0.25, 0.433, 0], [0.25, 0.433, 1], [0, 0.5, 0], [0, 0.5, 1], [-0.25, 0.433, 0], [-0.25, 0.433, 1], [-0.433, 0.25, 0], [-0.433, 0.25, 1], [-0.5, 0, 0], [-0.5, 0, 1], [-0.433, -0.25, 0], [-0.433, -0.25, 1], [-0.25, -0.433, 0], [-0.25, -0.433, 1]],
        "faces" : [[0, 4, 2], [2, 4, 5, 3], [1, 3, 5], [0, 6, 4], [4, 6, 7, 5], [1, 5, 7], [0, 8, 6], [6, 8, 9, 7], [1, 7, 9], [0, 10, 8], [8, 10, 11, 9], [1, 9, 11], [0, 12, 10], [10, 12, 13, 11], [1, 11, 13], [0, 14, 12], [12, 14, 15, 13], [1, 13, 15], [0, 16, 14], [14, 16, 17, 15], [1, 15, 17], [0, 18, 16], [16, 18, 19, 17], [1, 17, 19], [0, 20, 18], [18, 20, 21, 19], [1, 19, 21], [0, 22, 20], [20, 22, 23, 21], [1, 21, 23], [0, 24, 22], [22, 24, 25, 23], [1, 23, 25], [0, 2, 24], [24, 2, 3, 25], [1, 25, 3]],
        "matrix" : [
            (-0.0919, -0.0000,  0.2083, 0.1500),
            (-0.0000,  0.1200, -0.0000, 0.0000),
            (-0.0771,  0.0000, -0.2482, 0.7898),
            ( 0.0000,  0.0000,  0.0000, 1.0000)]
    }
}

def build_mesh(name):
    me = bpy.data.meshes.new(name)
    me.from_pydata(data[name]["verts"], [], data[name]["faces"])
    ob = bpy.data.objects.new(name, me)
    bpy.context.collection.objects.link(ob)
    return ob


cube = build_mesh("Cube")
cube.matrix_world = Matrix(data["Cube"]["matrix"])
print("\nCUBE before")
print(cube.matrix_world)
bpy.context.view_layer.update()
print("CUBE after")
print(cube.matrix_world)

cyl = build_mesh("Cylinder")
cyl.parent = cube
cyl.matrix_world = Matrix(data["Cylinder"]["matrix"])
print("\nCYLINDER before")
print(cyl.matrix_world)
bpy.context.view_layer.update()
print("CYLINDER after")
print(cyl.matrix_world)

In this script, bpy.context.view_layer.update() changes the world matrix of the cylinder. Before the update, it is

<Matrix 4x4 (-0.0919, -0.0000,  0.2083, 0.1500)
            (-0.0000,  0.1200, -0.0000, 0.0000)
            (-0.0771,  0.0000, -0.2482, 0.7898)
            ( 0.0000,  0.0000,  0.0000, 1.0000)>

and after the update

<Matrix 4x4 (-0.0747, 0.0000,  0.1819, 0.1500)
            ( 0.0000, 0.1200,  0.0000, 0.0000)
            (-0.1143, 0.0000, -0.2952, 0.7898)
            ( 0.0000, 0.0000,  0.0000, 1.0000)>

Why?? It only happens with the 3x3 part when both scaling and rotations are involved.

Well I think it’s because you are parenting the cylinder to the cube and after updating you are getting the result of the cube parent transforms having been applied to your cylinder. Try running your script with and without parenting the cylinder to the cube to view the transforms that are being applied to it.

You would think simply parenting the cylinder after updating would solve it but you need to perform another step…while I knew it had something to do with the parent inverse matrix I did have to google for the answer which I found on this page

cyl.parent = cube
cyl.matrix_parent_inverse = cube.matrix_world.inverted()

Yes, that was it. Thank you.