Notifications
Clear all

[Closed] MXS Array and its deepcopy

delete as a duplicate

9 Replies

Copy and Deep Copy have always being confused; and for some developers, never explained.
What does Copy do? and What does Deep Copy do? where to use copy and where deep copy? let’s see the difference:

Copy: just constructs a new object of your given, and then assigns all the referenced values of your given object.
Deep Copy, instead constructs a new object of your given, and also creates new object for each value, and nothing will be referenced. Their are 2 different object now, and not sharing anything.

As you may already know, many Autodesk products (including 3DS Max and Maya) can be coded using Python. I believe that this copy and deep copy comes from Python.
Here is a more technical and clear explaination:
https://docs.python.org/2/library/copy.html

Except this doesn’t work…


myarray = #(1,2,3,#("a","b","c"))
newArray = deepcopy myarray
newArray[4][3] = "banana"
print myarray[4][3]
--returns "banana"

it illustrates what i say above… it seems like DeepCopy just calls Copy method for every item (value) of Array. because simple Copy of an array value is not a “deep” copy you see the result you have

Actually I got muddled… that works correctly…

It’s when you have an array in a struct that doesn’t work with deepcopy.

I’m always getting headaches with deep copy… these are my most annoying bug finds…


struct s1 (
   myValA = #(1,2,3),
   myValB = #(#(1,1),#(2,2)),
   myValC = 4
)
--instantiate our struct
s = s1()

arrayOfStructs = #(s,s) --two instances of the same struct

--we can test if they are the same....
arrayOfStructs[1] == arrayOfStructs[2]  --true

--Problem number 1
--Deepcopy breaks this link....
newArray = deepcopy arrayOfStructs
newArray[1] == newArray[2] --false - INCORRECT

--Problem number 2
--Deepcopy doesn't give a unique pointer to the array in an array
newArray[1].myValA[1] = 5
--Or array of arrays
newArray[1].myValB[1][1] = 2
--but it's fine with non array values.
newArray[1].myValC = 44

--The Deep copy shouldn't have changed the original 's' value.
print s.myValA[1]  --5 - INCORRECT
print s.myValB[1][1] --2 - INCORRECT
print s.myValC -- 4 - CORRECT

I have functions to fix the 2 above problems, but maybe we can see if there’s better ways of doing so?

Anything more elegant? This is a bit of a bodge/hack I put together in a hurry for a script once upon a time.


fn trueDeepCopy val =
(
   newVal = deepcopy val
   if classof val == Array do
   (
      for o in newVal do
      (
         if superclassof o == structDef do
         (
            for p in getpropNames o where (classof (getproperty o p)) == Array do 
            (
               setproperty o p (deepcopy (getproperty o p))
            )
         )
      )
      
      b = #{1..val.count}
         
      for i = 1 to val.count where b[i] do
      (
         for j = i to val.count where i != j and val[i] == val[j] do
         (
            b[j] = false
            newVal[j] = newVal[i]
         )
      )
   )
   newVal
)

An alternative solution from Larry @ Autodesk, which should consider all case scenarios.


fn fixedDeepCopy val mapping: =
(
   if mapping == unsupplied do
      mapping = datapair #() #()
   local newVal
   local index = findItem mapping.v1 val
   if index != 0 then
      newVal = mapping.v2[index]
   else if (iskindof val maxwrapper) then
      newVal = val
   else if classof val == Array then
   (
      newVal = #()
      newVal.count = val.count
      for i = 1 to val.count do
      (
         newVal[i] = fixedDeepCopy val[i] mapping:mapping
      )
   )
   else if (superclassof val) == structDef then
   (
      newVal = copy val
      for p in getpropNames newVal do
      (
         setproperty newVal p (fixedDeepCopy (getproperty newVal p) mapping:mapping)
      )
   )
   else if (classof val) == DataPair then
   (
      newVal = copy val
      for p in #(#v1, #v2) do
      (
         setproperty newVal p (fixedDeepCopy (getproperty newVal p) mapping:mapping)
      )
   )
   else
      newVal = copy val
   if index == 0 do
   (
      append mapping.v1 val
      append mapping.v2 newVal
   )
   newVal -- return value
)

Apart from Dictionaries…