Unexpected result setting a shear matrix as transform I am doing a visualization where I need to transform a sphere with a matrix. Easiest solution would be to just set the matrix as local transform but this gives me unexpected results. 

I was trying around a little and found out, that setting matrix_local, delta_... and operating on vertex positions directly yields very different results. As an example, I tried it with a simple shear matrix. Here are the results:

Using matrix on vertex positions


Using local transformation matrix


Using delta transform

This is the code:

import bpy
from mathutils import *

def transform_verts(context):
    obj = context.active_object
    mat = Matrix.Shear('XY', 4, (3,3))

    #this does what it is supposed to do
    for j in range(0, len(obj.data.vertices)):
        obj.data.vertices[j].co = mat @ obj.data.vertices[j].co

def transform_delta(context):
    obj = context.active_object

    # This does NOT work as intended :(
    # this deforms the sphere into an elongated Ellipsoide
    d_loc, d_rot, d_sc = Matrix.Shear('XY', 4, (3,3)).decompose()
    obj.delta_location = d_loc
    obj.delta_rotation_quaternion = d_rot
    obj.delta_scale = d_sc

def transform_matrix_local(context):
    obj = context.active_object

    # for unknown reasons it is not possible to do this with an object transformation matrix
    # setting matrix_local yields similar weird results as setting delta transform
    # I suppose it may have to do with Blender internally decomposing it
    obj.matrix_local = Matrix.Shear('XY', 4, (3,3))

class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        #transform_verts(context)
        #transform_delta(context)
        transform_matrix_local(context)
        return {'FINISHED'}


def register():
    bpy.utils.register_class(SimpleOperator)


def unregister():
    bpy.utils.unregister_class(SimpleOperator)


if __name__ == "__main__":
    register()

    # test call
    bpy.ops.object.simple_operator()


Why does setting the matrix_local also does something very different from what is intended? In my understanding, setting the matrix_local should do exactly the same as applying the matrix to vertex positions.

Is there a workaround for this issue that does not involve looping through vertices and setting the position manually? The above is just an example. My actual use case is a modal operator and right now I need to store the original vertex positions and loop through them each time modal is called. That is really slow compared to just setting the local transformation matrix.
If there isn't, any idea how to speed it up?

Edit: obj.data.transform() is not possible because I need to apply the matrix to the sphere each time. This matrix is not necessarily invertable, so I can't just do the inverse transformation from the previous call and then do the current transformation. I would only be able to use it if I can somehow restore the positions the vertices had before the transformation.
Mesh.transform

Can transform mesh directly by a matrix with Mesh.transform(matrix)  The matrix must be a 4x4.  Use in object mode. Update the mesh to see result.

Python console code, C = bpy.context

&gt;&gt;&gt; mat = Matrix.Shear('XY', 4, (3,3))
&gt;&gt;&gt; mat
Matrix(((1.0, 0.0, 3.0, 0.0),
        (0.0, 1.0, 3.0, 0.0),
        (0.0, 0.0, 1.0, 0.0),
        (0.0, 0.0, 0.0, 1.0)))

&gt;&gt;&gt; C.object.data.transform(mat)
&gt;&gt;&gt; C.object.data.update()



  But this is transforms the vertices for good. In the modal operator, I
  need to apply it to the original positions of the sphere on each
  update. And the matrix is not necessarily invertable, so I can't just
  do the inverse beforehand, which makes it tricky


Shear matrix is not orthogonal, this is why it is not seen as an object matrix, and only in edit mode.

&gt;&gt;&gt; mat.is_orthogonal
False


AFAIK will need to apply to verts to shear.

Some ways to copy mesh coords, swap out a mesh copy

&gt;&gt;&gt; me = C.object.data
&gt;&gt;&gt; me2 = C.object.data.copy()
&gt;&gt;&gt; me2.transform(mat)
&gt;&gt;&gt; C.object.data = me2 # see transform
&gt;&gt;&gt; C.object.data = me # back to original
&gt;&gt;&gt; bpy.data.meshes.remove(me2) # remove it when done


Foreach set and get

load the 3d vert coordinates into a ravelled array.  Have used numpy as the matrix algebra could be performed on it reshaped, although will find Mesh.transform is quick too.

&gt;&gt;&gt; import numpy as np
&gt;&gt;&gt; data = np.zeros(len(me.vertices) * 3)
&gt;&gt;&gt; me.vertices.foreach_get("co", data)
&gt;&gt;&gt; data
array([ 0.00000000e+00,  1.95090324e-01,  9.80785251e-01, ...,
        1.51397259e-08,  1.95089921e-01, -9.80785310e-01])


The data is ravelled, here it is reshaped to display the coordinates 

&gt;&gt;&gt; np.reshape(data, (-1, 3))
array([[ 0.00000000e+00,  1.95090324e-01,  9.80785251e-01],
       [ 0.00000000e+00,  3.82683456e-01,  9.23879504e-01],
       [ 0.00000000e+00,  5.55570245e-01,  8.31469595e-01],
       ...,
       [ 1.52975474e-07,  7.07106352e-01, -7.07106769e-01],
       [ 7.84696610e-08,  3.82682920e-01, -9.23879623e-01],
       [ 1.51397259e-08,  1.95089921e-01, -9.80785310e-01]])


Transform the data, update to see result

&gt;&gt;&gt; me.transform(mat)
&gt;&gt;&gt; me.update()


write back, update to see result.

&gt;&gt;&gt; me.vertices.foreach_set("co", data)
&gt;&gt;&gt; me.update()


Another OTT method would be to make a shape key and transform that.