3
$\begingroup$

I'm trying to create an operator that takes as an input a matrix (ideally a numpy matrix) and creates and edits several objects based on the size and values of said matrix.

As far as I understand, the property types I can use for an operator are those of the type "FloatProperty", "StringProperty", etc, so there is no direct way to pass the matrix object ("FloatVectorProperty" is the closest thing I have found). How would you go in encapsulating the information inside the matrix (maybe in a list form, string, ...) so that I can pass it to the operator?

Thanks!

$\endgroup$
4

1 Answer 1

1
$\begingroup$

I think I found a way, it is a bit awkward, but it works ¯_(ツ)_/¯

import numpy as np
import bpy
from mathutils import Matrix

import re
import ast


class SimpleOperator(bpy.types.Operator):
    """Tooltip"""

    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"

    matrix_str: bpy.props.StringProperty()
    matrix = None

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

    @staticmethod
    def get_arr_from_str(string):
        string = re.sub(r'(\.\s)', '.0,', string)
        string = re.sub(r'\n', ',', string)
        return np.array(ast.literal_eval(string))

    def set_vectors(self):
        # get the matrix as an np.array()
        vecs = self.get_arr_from_str(self.matrix_str)
        print(vecs)
        # set the whole matrix as an attribute
        self.matrix = Matrix(vecs)
        for i, v in enumerate(vecs):
            # loop through the matrix and set each vector
            # as its own attribute
            # should work with arbitrary length matrices
            attrib = f"vector_{i+1}"
            setattr(self, attrib, v)

    def invoke(self, context, event):
        self.set_vectors()
        return self.execute(context)

    def execute(self, context):
        print("MATRIX ", self.matrix)
        print("VECTOR_ATTR ", self.vector_1)
        print("VECTOR_ATTR ", self.vector_2)
        return {'FINISHED'}


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


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


if __name__ == "__main__":
    register()

    obj = bpy.context.object
    mw = obj.matrix_world
    arr = str(np.array(mw))
    print(arr)
    bpy.ops.object.simple_operator("INVOKE_DEFAULT", matrix_str=arr)

#    returns:
#    MATRIX  <Matrix 4x4 (1.0000, 0.0000, 0.0000, 0.0000)
#            (0.0000, 1.0000, 0.0000, 0.0000)
#            (0.0000, 0.0000, 1.0000, 0.0000)
#            (0.0000, 0.0000, 0.0000, 1.0000)>
#    VECTOR_ATTR  [1. 0. 0. 0.]
#    VECTOR_ATTR  [0. 1. 0. 0.]
$\endgroup$
3
  • 1
    $\begingroup$ Great, this worked really well! Thanks! I struggled at the beginning because it was working with blender native matrices but not numpy ones, but I just had to set the entries in the matrix as floats with mat.astype(float). I also considered doing something similar passing the whole matrix as a vector, instead of a string, but then I'd need to also pass the dimensions, which in your solution are encoded in the string itself. Thanks a lot again! $\endgroup$
    – Grub
    Commented Jul 4, 2022 at 22:28
  • 1
    $\begingroup$ @Grub better stick with the native ones because according to the numpy documentation: "It is no longer recommended to use this class (numpy.matrix), even for linear algebra. Instead use regular arrays. The class may be removed in the future." numpy.org/doc/stable/reference/generated/numpy.matrix.html $\endgroup$
    – Harry McKenzie
    Commented Jul 5, 2022 at 1:49
  • $\begingroup$ Good to know, thanks @HarryMcKenzie! $\endgroup$
    – Grub
    Commented Jul 5, 2022 at 12:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .