Home Technical Talk

Selection as Array variable not keeping it values..

I'm working on a script, and I need the OldP = selection in an array to keep its values so I can re-call the objects positions later and move them back to where they used to be. But for some reason that variable gets changed to their new pos after I move them.....

Does anyone know what I'm doing wrong?? :(

global oldP = selection as array
 local cnt = 0
                            
                                for obj in selection where selection.count > 0 do
                                (
                                    --stores objects as array so we can get restore there positions later
                                    cnt += 1
                                    print oldP[cnt].position
                                    
                                    --sets the selections pivots to the averaged center point, and moves them to 0,0,0 as a group average.
                                    selection.pivot  = selection.center
                                    selection.position = [0,0,selection.center[3]]
                                )
                    )
                    else 
                    (
                            local cnt = 0
                        
                            for obj in selection where selection.count > 0 do
                            (
                                cnt += 1
                                obj.position = oldP[cnt].position
                                print oldP[cnt]
                                
                                --obj.pivot = obj.position
                            )


I would really appreciate any input. :)

M

Replies

  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    you need to iterate through your selection and copy the position property into a separate array. Something like:
    //plugs in programming keyboard...
    posArray = #();
    for o in selection do(
    append posArray (deepCopy o.pos);
    )
    
    I am not sure if pos will be referenced here instead of copies (like simple values) so thats why I deep copied them. Later in your script you just reference to them using a for i=.. loop instead of the one I used so that you can use: posArray where i is the iterator.
  • SyncViewS
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi Matt, by looking at your code there are some conceptual errors in the use of the selection collection and the for loop. Could you please explain what's the purpose of this script so I can give you a tip on it?
    If I get it right you want to temporarily shift all selected objects to an average plane along the Z axis, print their positions and later shift them back to their original positions. Does it make sense? Could you post a complete block of code like the whole if..else?
  • mgrd
    Offline / Send Message
    mgrd polycounter lvl 18
    I'm not 100% sure what you are trying to do, but for what I'm guessing, using a struct to store the information would be the safest way.

    When you do oldP = (selection as array) each item in there is a reference to the actual object in the scene. When you change the pivot and position of each object using Selection.pivot = ... you are changing the same object that is stored in oldP.

    I wrote something that demonstrates how to use a struct to store and restore data using your code posted, I'm pretty sure this is the effect you were going for.
    struct objData (
    	init = 0, -- Init
    	obj = undefined, -- Object Handle
    	oldPosition = [0, 0, 0], -- Old Position
    	oldPivot = [0, 0, 0] -- Old Pivot
    )
    
    -- Array of objData structs
    objectData = #()
    
    fn storeData &objectData = 
    -- Pass in object data as a reference
    (
    	-- Convert the selection to an array
    	selectionArray = (selection as array)
    
    	-- Get the selection pivot
    	selectionPivot = selection.center
    
    	-- Get the selection center
    	selectionCenter = [0, 0, selection.center[3]]
    	
    	-- Store the data
    	for currentItem in selectionArray do (
    		-- Build the struct
    		tmpStruct = objData init:1
    		
    		-- Fill in the struct
    		
    		-- Set the object
    		tmpStruct.obj = currentItem
    		
    		-- Set the old position
    		tmpStruct.oldPosition = currentItem.position
    		
    		-- Set the old pivot
    		tmpStruct.oldPivot = currentItem.pivot
    		
    		-- Sets the selections pivots to the averaged center point, and moves them to 0,0,0 as a group average.
    		currentItem.position = selectionCenter
    		currentItem.pivot = selectionPivot
    		
    		-- Append the struct to the objectData array
    		append objectData tmpStruct
    	)
    )
    
    fn restoreData objectData =
    (
    	-- Reset the data
    	for currentObjectData in objectData do (
    		-- Get the object
    		currentObject = currentObjectData.obj
    			
    		-- Reset the position
    		currentObject.position = currentObjectData.oldPosition
    		
    		-- Reset the pivot
    		currentObject.pivot = currentObjectData.oldPivot		
    	)
    )
    

    To use this select your object in the scene and call the function: storeData &objectData

    & is a symbol that tells maxscript to pass by reference instead of by value. For more information on pass by reference and pass by value you can look here http://cplus.about.com/od/glossar1/g/passbyrefdefn.htm

    To simplify it though, passing by reference tells the function to not make a copy of the variable and edit it directly, pass by value, which is the default for most maxscript variables, will make and use a copy of the variable passed in. Keep in mind that not all variables passed into a function are being passed by value, object handles are an example of a variable that is always passed by reference.

    Ok, got a little off tangent there, but the second part of script is a function called: restoreData. To restore your objects to their original pivot and position call the function: restoreData objectData

    Notice, as we are not editing the variable there is no reason to pass this by reference.
  • Piflik
    Offline / Send Message
    Piflik polycounter lvl 12
    Yeah...the .pos property is a reference. I is always updated with the current values. If you just store the .pos in an array, move the object and then try to restore the old position, the array will not have the old position stored, but the current one...you'll have to copy it.

    example:
    Pos = for o in selection collect o.pos -- collects position references
    OldPos = for o in selection collect (copy o.pos) -- collects independent Point3 values
    
  • mLichy
    Wow, sweet. Thanks for all the suggestions/ideas.

    The script idea was asked by a fellow artist, if I could make something to take a selection of object(s), and temporarily move them to 0,0,0, or in general around that point, then move them back to where they were when the button is deactivated.

    I'll checkout what u guys have posted and see what works best for what I need. But I'll keep all the info. u guys supplied for further use/research :D


    Edit:

    Ok...So I think what render has might work fine, it's simple enough, although I get the error after copying the first objects position "unable to convert:[-2.31057,-9.35394,0] to type: Array

    mgrd: Your's doesn't quite work how I was planning, but it did move each object to 0,0,0. I was planning to move the whole selection. But the issue with the code was that when I did move back, it moved back the object pivot, but not the mesh itself :S.


    Pilflik: I tried yours as well, and maybe I didn't call them right... but I evaluated both of those lines, then moved the objects, and printed out the oldPos, and it said 0,0,0.
  • mLichy
  • Piflik
    Offline / Send Message
    Piflik polycounter lvl 12
    Hmm...works for me...surprisingly the one without copy does, too...seems like it doesn't store references after all...
  • SyncViewS
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi Matt, this is my attempt at your code. Let me know if it is what you're looking for. Keep in mind that original position is accessible only until the floating dialog is open.
    (
        -- define a rollout
        rollout rolSelPosSwitcher "Pos"
        (
            -- create a checkbutton in the rollout
            checkButton btSwitch "Switch!" width:40 height:20 pos:[2, 2]
    
            -- variable to store the selection array
            local theSelStore = #()
            -- variable to store the selection center
            local p3SelCenter = #()
    
            -- button event
            on btSwitch changed bState do
            (
                if (bState) then -- when button is pressed
                (
                    -- store current selection
                    theSelStore = selection as Array
                    -- get current selection center
                    p3SelCenter = selection.center
                    
                    -- shift all nodes in the selection by the selection center position
                    -- it is like shifting the whole selection to the origin
                    for theNode in theSelStore do
                        theNode.pos -= p3SelCenter
                )
                else -- when button is released
                (
                    -- shift all nodes in the stored selection back by the offset value
                    for theNode in theSelStore do
                        if (isValidNode theNode) do -- deal with deleted nodes
                            theNode.pos += p3SelCenter                
                )
            )
        )
        
        -- create the floating dialog
        createDialog rolSelPosSwitcher 44 24 style:#(#style_toolwindow, #style_sysmenu, #style_resizing)
    )
    
  • mLichy
    Dude, yeah, that's awesome :D. Huh... it seems fairly straight forward... So the key to storing info. properly is to use 2 arrays...hmm

    So after testing it, like u mentioned, I have to call that dialog to get it to properly work? Maybe I'll just have a button my toolbar create/destroy that little button then to make sure it all works as expected.

    thank you very much :D
  • SyncViewS
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi Matt, here is the MacroScript version, with some added checks. Change the heading as you like. After evaluation, you can associate it to a shortcut or a toolbar button.
    macroScript PositionSwitcher
    category:"Test Tools"
    buttonText:"PosSwitch"
    tooltip:"Position Switcher"
    (
        -- variable to store the selection array
        local theSelStore = #()
        -- variable to store the selection center
        local p3SelCenter = #()
    
        -- define a rollout
        rollout rolSelPosSwitcher "Pos"
        (
            -- create a checkbutton in the rollout
            checkButton btSwitch "Switch!" width:40 height:20 pos:[2, 2]
    
            -- button event
            on btSwitch changed bState do
            (
                if (bState) then -- when button is pressed
                (
                    -- store current selection
                    theSelStore = selection as Array
                    -- get current selection center
                    p3SelCenter = selection.center
    
                    -- doesn't allow to press the button if nothing is selected
                	if (theSelStore.count == 0) then
                	    btSwitch.checked = false
                    else
                        -- shift all nodes in the selection by the selection center position
                        -- it is like shifting the whole selection to the origin
                        for theNode in theSelStore do
                            theNode.pos -= p3SelCenter
                )
                else -- when button is released
                (
                    -- shift all nodes in the stored selection back by the offset value
                    for theNode in theSelStore do
                        if (isValidNode theNode) do -- deal with deleted nodes
                            theNode.pos += p3SelCenter
    
                    theSelStore = #()
                )
            )
    
            -- trigger the shift back if rollout is closed while button is pressed
            on rolSelPosSwitcher close do
            (
                if (theSelStore.count > 0) do
                    btSwitch.changed false
            )
        )
    
        on execute do
        (
            try ( destroyDialog rolSelPosSwitcher ) catch ()
            createDialog rolSelPosSwitcher 44 24 100 200 style:#(#style_toolwindow, #style_sysmenu, #style_resizing)
        )
    )
    
  • mLichy
    Awesome man, even better :D. I might modify it a tiny bit if I can get it to work without breaking your script, so it uses the centers x,y, but uses the min z of the selection.

    You don't have to worry about it though, I'll figure it out... I hope, ha. I just want to do that so the selection doesn't sit halfway through the grid in there z pos.


    Edit: Got that working, so all is sweetness :D. Now to polish up a couple last things on my tools :D
  • SyncViewS
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi Matt, here is another version, I almost forgot you can make MacroScripts work like toggle buttons. This code avoid the obnoxious rollout, and can be directly associated to a shortcut or a toolbar button. Modify it as you need.
    macroScript PositionSwitcher
    category:"Test Tools"
    buttonText:"PosSwitch"
    tooltip:"Position Switcher"
    (
        -- variable to store the selection array
        local theSelStore = #()
        -- variable to store the selection center
        local p3SelCenter = #()
    
        function switch bState =
        (
            if (bState) then -- when button is pressed
            (
                -- store current selection
                theSelStore = selection as Array
                -- get current selection center
                p3SelCenter = selection.center
    
                -- shift all nodes in the selection by the selection center position
                -- it is like shifting the whole selection to the origin
                for theNode in theSelStore do
                    theNode.pos -= p3SelCenter
            )
            else -- when button is released
            (
                -- shift all nodes in the stored selection back by the offset value
                for theNode in theSelStore do
                    if (isValidNode theNode) do -- deal with deleted nodes
                        theNode.pos += p3SelCenter
    
                theSelStore = #()
            )
        )
    
        on isChecked do
            try ( return (theSelStore.count != 0) ) catch ( false )
    
        on execute do
            try ( switch (theSelStore.count == 0) ) catch ()
    )
    
  • renderhjs
    Offline / Send Message
    renderhjs sublime tool
    mLichy wrote: »
    ...Now to polish up a couple last things on my tools :D
    and keep writing more maxscript, we could use some more maxscripters here in the tech talk section.
Sign In or Register to comment.