2
$\begingroup$

When I created a matrix transformation in the console as follows, blender file

>>> c2=bpy.data.objects["Cube.001"].matrix_world
>>> c1=bpy.data.objects["Cube.002"].matrix_world
>>> c1
Matrix(((0.2511853277683258, -0.9679380655288696, 0.0, -47.04343032836914),
    (0.9679380655288696, 0.2511853277683258, 0.0, 18.430648803710938),
    (0.0, 0.0, 1.0, 0.0),
    (0.0, 0.0, 0.0, 1.0)))

>>> c2
Matrix(((-0.7805679440498352, 0.6250709295272827, 0.0, -10.104384422302246),
    (-0.6250709295272827, -0.7805679440498352, 0.0, 6.641378402709961),
    (0.0, 0.0, 1.0, 0.0),
    (0.0, 0.0, 0.0, 1.0)))

>>> bpy.context.active_object.matrix_world = c1*c2
>>> h=c1*c2
>>> bpy.context.active_object.matrix_world = h

the graphical results of the 3D screen on the active object were different when keep repeating the same line like:

>>>bpy.context.active_object.matrix_world = c1*c2    
>>>bpy.context.active_object.matrix_world = c1*c2
>>>bpy.context.active_object.matrix_world = c1*c2

The object keeps incrementally moving but another one of the following with a resultant h would't move, like the following:

>>>h=c1*c2
>>>bpy.context.active_object.matrix_world = h
>>>bpy.context.active_object.matrix_world = h
>>>bpy.context.active_object.matrix_world = h

I wonder it is a bug or something?

$\endgroup$
  • $\begingroup$ That's strange. I tried it, but don't get that kind of result. Active object moves after the first assignment only, regardless of whether I assign c1 * c2 or h to the matrix_world. Is it possible for you to attach the blend file with the test objects? $\endgroup$ – Blender Dadaist Oct 30 '18 at 11:03
  • $\begingroup$ Yes, I can attach the file. Can I attach it to StackExchange? $\endgroup$ – PatrickP Oct 30 '18 at 12:44
  • $\begingroup$ You can attach here: blend-exchange.giantcowfilms.com $\endgroup$ – Blender Dadaist Oct 30 '18 at 14:36
  • $\begingroup$ Attached as follows: <img src="https://blend-exchange.giantcowfilms.com/embedImage.png?bid=5463" />. BTW, it looks someone has answered my problem. Anyway, the problem cannot repeat and didn't happen again just like the Schrodinger's Cat!! :P $\endgroup$ – PatrickP Oct 31 '18 at 7:05
2
$\begingroup$

Playing around, it seems that the assignment

c2 = bpy.data.objects["Cube.002"].matrix_world

is not making a deep copy.

Poking into the matrix elements, this kind of result can arise after making the assignment:

>>> bpy.data.objects["Cube.002"].matrix_world[0][0] is c2[0][0]
False

but....

>>> id(bpy.data.objects["Cube.002"].matrix_world[0][0])
2569715751792

>>> id(c2[0][0])
2569715751792

.. which seems odd? I'm very much hoping that a more experienced Pythonista will come along and say this is OK.

It certainly seems to be true, after assignment, that any direct manipulation of the elements of c2 results in identical changes to the elements of bpy.data.objects["Cube.002"].matrix_world, and vice-versa. So, (after the assignment,) an expression like:

bpy.data.objects["Cube.002"].matrix_world = c1 * c2

is modifying the contents of c2 as well as bpy.data.objects["Cube.002"].matrix_world, explaining the incremental moving.

$\endgroup$
  • 1
    $\begingroup$ Worth mentioning mw = ob.matrix_world.copy() to obtain a snapshot on creation would fulfill the expectation of the questioners code. References like this can also produce segment faults if the matrix is accessed after the object is removed. $\endgroup$ – batFINGER Oct 31 '18 at 10:06
  • 1
    $\begingroup$ ... oops and meant to add, effectively that last line of code could also be written c2 = c1 * c2 $\endgroup$ – batFINGER Oct 31 '18 at 10:18
  • $\begingroup$ @batFinger ... which is a much snappier way of answering the OP. So the copy() being called by your first comment is bpy's ID.copy()? ... which although it doesn't say so in the docs, must be making a deep copy?.. I guess? Whatever, it will be the safest way to go. $\endgroup$ – Robin Betts Oct 31 '18 at 13:31
  • 1
    $\begingroup$ copy for some reason Matrix.copy() doesn't have the note re removing reference to the original data. The same applies with any property that is an instance of a mathutils class Vector, Matrix, Color... $\endgroup$ – batFINGER Oct 31 '18 at 13:43
  • $\begingroup$ @Robin Betts Thanks for your suggestion of the deep copy and shallow copy. This may be the keyword to this problem. The result is not definite and make me feel something wrong or imperfect because I tried again the "incremental situation" does not repeat again. Just intuitively remind me of Blender python version's quantum entanglement? :P $\endgroup$ – PatrickP Nov 1 '18 at 4:29
4
$\begingroup$

enter image description here I think the two answers above pretty much explain what's happening. In case of assignments, we need to understand what's being assigned. Is it value or reference to the memory location (in C, it's called pointer)?

When one does an assignment like:

c1 = bpy.data.objects["Cube.002"].matrix_world

one essentially stores the reference of matrix_world in c1. So if c1 changes, matrix_world would change and vice versa. Since the active object in this case happens to be (I guess) one of the two cubes, let's say Cube.001, whose matrix world references is held in one of the two references (c2), the assignment to that objects matrix_world with the following line:

bpy.context.active_object.matrix_world = c1 * c2

changes the value referenced by matrix_world. But this is also the value referenced by c2. So when the same statement is executed again c2's value is already changed. This should explain the incremental movement. (I missed this point before since I was thinking the active object is not one of Cube.001 and Cube.002 but the original Cube :).

This does not hold true for h, it's a new memory location, which contains the (static) product of the two matrices. This is static in this particular case since none of the operations are manipulating the value referenced by h. So no matter how many times you assign h to the matrix_world the object is not going to change.

$\endgroup$
  • 1
    $\begingroup$ UV for making it clear with pictures :) .. however I'm still confused.. by the results of the Python is operator and id() function. For me, is never returns True, suggesting references to distinct objects. However id() returns identical results at the third level of dereference (to matrix elements) and sometimes to the second level ( Vector matrix rows ) and never to the first (the matrices themselves). The result of id() can change over time, even with no activity, suggesting that it's derived from memory addresses which are changing due to background memory management? $\endgroup$ – Robin Betts Oct 31 '18 at 10:00
  • 1
    $\begingroup$ Thanks for the UV :) I shall attempt to summarize my understanding (open to corrections) : Let's say the actual 4 X 4 matrix is at location xx in memory. obj.matrix_world is at location yy and it refers (or points) to xx. With the assignment c = obj.matrix_world, we aallocate a new memory space zz, stored in c but pointing to xx. So xx can be changed through c or matrix_world. But the "actual values" of c and matrix_world are zz and yy respectively (the two id() values). So c is obj.matrix_world won't be True whereas c == obj.matrix_world will. $\endgroup$ – Blender Dadaist Oct 31 '18 at 11:22
  • 1
    $\begingroup$ @RobinBetts scottlobdell.me/2013/08/… $\endgroup$ – Blender Dadaist Oct 31 '18 at 11:24
  • $\begingroup$ Thanks! .. My understanding of your understanding :) is that there's another layer of indirection above the ones I've been assuming in my answer...this conversation really would need pictures.. I'll wait for experience to teach me a bit more. All this Python is pointing to C++ classes, anyway? Whatever..when in doubt, @batFINGER's copy() recommendation seems good. $\endgroup$ – Robin Betts Oct 31 '18 at 13:38
1
$\begingroup$

when you use structs like a matrix resulting from h=c1*c2 the value of this multiplication would be stored in h but when you use c1*c2 equivalently you are assigning this:

bpy.context.active_object.matrix_world=bpy.data.objects["Cube.002"].matrix_world *
                                       bpy.data.objects["Cube.001"].matrix_world

so you will get different results if the active object be one one of these two cubes.

but if you use the value of this multiplication this will never change:

c1 = bpy.data.objects["Cube.002"].matrix_world
c2 = bpy.data.objects["Cube.001"].matrix_world

h=c1*c2

bpy.context.active_object.matrix_world=h

because now h is a simple struct

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.