PDA

View Full Version : Copy/Paste transform data?


Malkalypse
04-01-2009, 03:04 AM
I want to write a script that will let me do the following:

1. Select a bunch of objects, click a button, and thereby put the name of each object along with its transform data into some kind of text file.

2. Open up a new max file, load the info from that text file into buttons on a rollout, so that when I select an object and click the button with whatever name, it will paste the transform data from that named object into the object that is selected.

I'm sure I could figure out how to do something like this on my own, but it would me a long time and would probably be a big mess on top of it. What is the SIMPLEST way for me to do what I am trying to accomplish?

Bobo
04-01-2009, 03:31 AM
Your description of the process is pretty clear, which is the best first step in writing a good script.
The saving of the data is rather straight-forward. I would suggest using an INI file (setIniSetting(), getIniSetting()) since it performs most of the file management, reading and writing pretty much automatically.

You could alternatively store the info in memory in a global array if you are not going to use the data between Max sessions.

The tricky part is the UI portion. The simplest way would be to populate a ListBox with the names+transforms and use the doube-click handler of the ListBox to assign to whatever is selected. This will remove the need to build a UI procedurally with a button for each object etc. Not that you couldn't do that, but it would be an easier Proof Of Concept.

Go on, try it out and ask questions if you hit a wall...

LoneRobot
04-01-2009, 10:09 AM
UI's always take forever for this sort of thing. I am currently using XML to to store transforms and using this (http://forums.cgsociety.org/showthread.php?f=98&t=745287) dynamic control to display the preset files. If you were to store each transform as a separate file, it would work well.

However, for your particular problem, it would probably be better to store in a single text file or XML file, and read the data as an array, like you stated. You could still use the FlowLayoutPanel to generate the ui, and store the Transform in the tag property of each button on the layoutpanel as it is generated.

try this as a start -

NodeArray = #()

-- create some stuff to store

for i = 1 to 20 do
(
sphere radius:2 position:[(random 0 100),(random 0 100),(random 0 100)] wirecolor:(color (random 0 255) (random 0 255) (random 0 255))
cone radius:2 position:[(random 0 100),(random 0 100),(random 0 100)] wirecolor:(color (random 0 255) (random 0 255) (random 0 255))
cylinder radius:2 position:[(random 0 100),(random 0 100),(random 0 100)] wirecolor:(color (random 0 255) (random 0 255) (random 0 255))
)

NodeArray = for o in objects collect o

try (destroydialog flctest ) catch()

rollout flctest "TM Storage" width:256 height:408
(
local maxBackColor = colorMan.getColor #background
local maxButtonColor = colorMan.getColor #pressedButton
local maxbackDNcol = (DotNetClass "System.Drawing.Color").FromArgb (maxBackColor[1] * 255.0f) (maxBackColor[2] * 255.0f) (maxBackColor[3] * 255.0f)
local maxbuttonDNcol=(DotNetClass "System.Drawing.Color").FromArgb (maxButtonColor[1] * 255.0f) (maxButtonColor[2] * 255.0f) (maxButtonColor[3] * 255.0f)
local ButtonSize = [75,30]

fn buttonhandler sender args =
(
for i = 0 to flctest.flc.controls.count-1 do
(
if (((flctest.flc.controls.item[i].gettype()).name) == "Button") do
(
flctest.flc.controls.item[i].backcolor = maxbackDNcol
)
)
sender.backcolor = (DotNetClass "System.Drawing.Color").Chartreuse

-- apply the transform from the selected button tag

if selection.count == 1 do (selection[1].transform = execute sender.tag)


)

fn AddFlowLayoutNodes control nodes buttonsizex buttonsizey=
(
for i = 1 to nodes.count do
(
flowbutton = dotnetobject "button"
flowbutton.font = dotNetObject "System.Drawing.Font" "Verdana" 7 ((dotNetClass "System.Drawing.FontStyle").regular)
flowbutton.backcolor = maxbackDNcol
flowbutton.Margin = dotnetobject "System.Windows.Forms.Padding" 1
flowbutton.flatstyle = (dotNetclass "System.Windows.Forms.FlatStyle").flat
flowbutton.size = dotnetobject "System.Drawing.Size" buttonsizex buttonsizey
flowbutton.FlatAppearance.MouseDownBackColor = maxbuttonDNcol
flowbutton.textalign = (dotnetclass "System.Drawing.ContentAlignment").MiddleCenter
flowbutton.text = Nodes[i].name
flowbutton.tag = Nodes[i].transform as string
dotnet.addeventhandler flowbutton "Click" buttonhandler
control.Controls.add flowbutton
if i == nodes.count do (return flowbutton)
)
dgc = dotnetclass "system.gc"
dgc.collect()
gc()
)

fn addseparator control title =
(
spacelabel = dotnetobject "label"
spacelabel.textalign = (dotnetclass "System.Drawing.ContentAlignment").bottomCenter
spacelabel.font = dotNetObject "System.Drawing.Font" "Verdana" 7 ((dotNetClass "System.Drawing.FontStyle").bold)
spacelabel.size = dotnetobject "System.Drawing.Size" (control.ClientRectangle.Right-19) 16
spacelabel.backcolor = (DotNetClass "System.Drawing.Color").gray
spacelabel.Margin = dotnetobject "System.Windows.Forms.Padding" 1
spacelabel.borderstyle = (dotNetClass "System.Windows.Forms.BorderStyle").fixedsingle
spacelabel.text = title
control.Controls.add spacelabel
return spacelabel
)

dotNetControl FLC "System.Windows.Forms.FlowLayoutPanel" pos:[2,4] width:250 height:400

on flctest open do
(
flc.borderstyle= (dotNetClass "System.Windows.Forms.BorderStyle").fixedsingle
flc.autoscroll = true
flc.backcolor = (DotNetClass "System.Drawing.Color").slategray
flc.SuspendLayout()

if NodeArray.count > 0 then
(
flc.setflowbreak (addseparator flc "Click To Set Transform") true
flc.setflowbreak (AddFlowLayoutNodes flc NodeArray buttonsize.x buttonsize.y) true
flc.resumelayout()
)

else flc.setflowbreak (addseparator flc "No TM Data") true
)
)
createdialog flctest style:#(#style_toolwindow, #style_sysmenu)

Only slightly ugly bit is the use of execute to retrieve the matrix3 value from the buttons tag property. I wanted to use DotNetMXSvalue to store the matrix3, but it appeared that in the loop to generate the buttons, it was updated the with the last stored TM value, so all tags read the same TM. strange one that, kind of useless unless i've done it wrong, which is probably likely.:shrug:

Bobo
04-01-2009, 01:32 PM
Pete, the question was about the SIMPLEST way to do it.
I wouldn't call XML saving the simplest way possible ;)
Elegant? Yes. But simplest?


macroScript CaptureTransforms category:"Forum Help"
(
local theIniFile = getDir #export + "\\_TransformData.ini"
deleteFile theIniFile
for o in selection do
setIniSetting theIniFile o.name "Transform" (o.transform as string)
)

macroScript ApplyCapturedTransforms category:"Forum Help"
(
global ApplyCapturedTransforms_Rollout
try(destroyDialog ApplyCapturedTransforms_Rollout) catch()
rollout ApplyCapturedTransforms_Rollout "Apply Transforms"
(
local theNames = #()
local theTransforms = #()
listbox lbx_transforms "Double-click to Apply To Selection" height:12
on lbx_transforms doubleClicked itm do
(
for o in selection do o.transform = execute theTransforms[itm]
)
on ApplyCapturedTransforms_Rollout open do
(
local theIniFile = getDir #export + "\\_TransformData.ini"
theNames = getIniSetting theIniFile
theTransforms = for i in theNames collect getIniSetting theIniFile i "Transform"
lbx_transforms.items = for i = 1 to theNames.count collect theNames[i] + " : " + theTransforms[i]
)
)
createDialog ApplyCapturedTransforms_Rollout 450 200
)

LoneRobot
04-01-2009, 01:44 PM
Hi Bobo,

isn't XML the same as a text file but with those funny little wiggly lines all over it? ;)

I take the point, but whether loading or saving through either method, I wanted to post the FlowlayoutPanel example just as an option for building the dynamic interface, ala RolloutCreator style. :thumbsup:

Bobo
04-01-2009, 01:52 PM
Hi Bobo,

isn't XML the same as a text file but with those funny little wiggly lines all over it? ;)

I take the point, but whether loading or saving through either method, I wanted to post the FlowlayoutPanel example just as an option for building the dynamic interface, ala RolloutCreator style. :thumbsup:

I like what you posted.
I am just saying it might go over the head of most people including the OP.
Since he asked for the simplest approach, I felt yours was space age technology compared to simple INI file storage. ;)

Hope I did not offend.

Kameleon
04-01-2009, 02:21 PM
Hehe Bobo was faster!

Here's my version, you can double click an item in the listbox like in Bobo's version or you can select Apply all and it applies the trasnformations to objects with the same name, maybe this could be useful too, who knows! Cheers.

macroScript ObjCopyPaste
category:"K Scripts"
(
rollout objdataT "ObjT Copy/Paste" width:300
(
label lbl_objects "Objects:" align:#left
dotnetcontrol lst_obj "System.Windows.Forms.Listbox" align:#center height:200
label lbl_transform "Transform:" align:#left width: 275 height:42 style_sunkenedge:true
button btn_save "Save" align:#left across:2
button btn_load "Load" align:#right
button btn_apply "Apply all"
button btn_site "?" align:#center
local iniPath=getdir(#export)+"\\objT.ini"

on objdataT open do
(
lst_obj.HorizontalScrollbar=True
)

on btn_save pressed do
(
if selection.count!=0 then
(
iniPath=getdir(#export)+"\\objT.ini"
fileSave=getSaveFileName filename:iniPath types:"Ini File(*.ini)"
if fileSave!=undefined then
(
deleteFile fileSave
for i in selection do
(
setINISetting fileSave "Object Transforms" i.name (i.transform as string)
lst_obj.Items.Add i.name
)
)
)
)

on btn_load pressed do
(
fileOpen=getOPenFileName filename:iniPath types:"Ini File(*.ini)"
if fileOpen!=undefined then
(
iniSection=getIniSetting fileOpen
iniSection=iniSection[1]
iniKey=getIniSetting fileOpen iniSection

for i=1 to iniKey.count do
(
lst_obj.Items.Add iniKey[i]
)
iniPath=fileOpen
lst_obj.SelectedIndex=0
)
)

on lst_obj SelectedIndexChanged sender arg do
(
iniSection=getIniSetting iniPath
iniSection=iniSection[1]
iniKey=getIniSetting iniPath iniSection lst_obj.SelectedItem
lbl_transform.text=iniKey
)

on lst_obj DoubleClick sender arg do
(
for i in selection do
(
i.transform=execute lbl_transform.text
)
)

on btn_apply pressed do
(
if lst_obj.Items.Count!=0 then
(
iniSection=getIniSetting iniPath
iniSection=iniSection[1]
iniKey=getIniSetting iniPath iniSection

for i=1 to iniKey.count do
(
try
(
execute ("select $" + iniKey[i])
iniTransform=getiniSetting iniPath iniSection iniKey[i]
execute ("$"+iniKey[i] + ".transform="+iniTransform)
)
catch (print ("Object " + iniKey[i] + " not found"))
)
)
)
on btn_site pressed do
(
process=dotnetclass "System.Diagnostics.Process"
process.start "http://www.dimensao3.com/al"
)
)
clearListener()
createDialog objdataT
)

LoneRobot
04-01-2009, 02:24 PM
Hope I did not offend.

Not at all! :cool:

nice solutions, people.

drdubosc
04-01-2009, 02:29 PM
Sometimes, I love this forum .... K.I.S.S.

Malkalypse
04-01-2009, 03:36 PM
Wow! Thanks everyone, I will get right on trying each of these solutions out! I'll even try the xml approach; maybe I will learn something.

Kameleon
04-01-2009, 04:37 PM
I guess I've found some limitation in the getIniSetting, I can't get past 1175 items :D Anyone knows something about this?

Edit: And using Bobo structure I can only get 1162 items :/

Kameleon
04-01-2009, 06:12 PM
OK, kind of solved it... did the XML version :D It's way faster than the INI version and it works at least with 2200 objects in a matter of seconds (saving and loading). Cheers!

macroScript ObjCopyPaste
category:"K Scripts"
(
try (destroyDialog ObjCopyPaste_Rollout) catch()

rollout ObjCopyPaste_Rollout "ObjT Copy/Paste" width:300
(
label lbl_objects "Objects:" align:#left
dotnetcontrol lst_obj "System.Windows.Forms.Listbox" align:#center height:200
label lbl_transform "Transform:" align:#left width: 275 height:42 style_sunkenedge:true
button btn_save "Save" align:#left across:2
button btn_load "Load" align:#right
button btn_apply "Apply all"
button btn_site "?" align:#center
local xmlPath=getdir(#export) + "\\ObjCopyPaste.xml"
local XML=dotnetobject "System.Xml.XmlDocument"

on objdataT open do
(
lst_obj.HorizontalScrollbar=True
)

on btn_save pressed do
(
if selection.count!=0 then
(
xmlPath=getdir(#export) + "\\ObjCopyPaste.xml"
fileSave=getSaveFileName filename:xmlPath types:"XML File (*.xml)|*.xml"
if fileSave!=undefined then
(
lst_obj.BeginUpdate()
lst_obj.Items.Clear()
---
XML=dotnetobject "System.Xml.XmlDocument"
root=XML.CreateElement "Root"
XML.AppendChild root

for i in selection do
(
xName=XML.CreateElement i.name
xName.InnerText=i.transform as string
root.AppendChild xName
lst_obj.Items.Add i.name
)

XML.Save fileSave
---
lst_obj.EndUpdate()
)
)
)

on btn_load pressed do
(
fileOpen=getOPenFileName filename:xmlPath types:"XML File (*.xml)|*.xml"
if fileOpen!=undefined then
(
lst_obj.BeginUpdate()
lst_obj.Items.Clear()
---
XML.Load fileOpen

root=XML.FirstChild
for i=0 to root.ChildNodes.Count-1 do
(
lst_obj.Items.Add root.ChildNodes.Item[i].name
)

xmlPath=fileOpen
---
lst_obj.SelectedIndex=0
lst_obj.EndUpdate()
)
)

on lst_obj SelectedIndexChanged sender arg do
(
fNode=XML.FirstChild.GetElementsByTagName(lst_obj.SelectedItem)
lbl_transform.text=fNode.Item[0].InnerText
)

on lst_obj DoubleClick sender arg do
(
setCommandPanelTaskMode mode:#create
suspendEditing()
for i in selection do
(
i.transform=execute lbl_transform.text
)
resumeEditing()
)

on btn_apply pressed do
(
if lst_obj.Items.Count!=0 then
(
setCommandPanelTaskMode mode:#create
suspendEditing()
root=XML.FirstChild
for i=0 to root.ChildNodes.Count-1 do
(
try
(
execute ("select $" + root.ChildNodes.Item[i].name)
execute ("$"+ root.ChildNodes.Item[i].name + ".transform=" + root.ChildNodes.Item[i].InnerText)
)
catch (print ("Object " + root.ChildNodes.Item[i].name + " not found"))

)
resumeEditing()
)
)
on btn_site pressed do
(
process=dotnetclass "System.Diagnostics.Process"
process.start "http://www.dimensao3.com/al"
)
)
clearListener()
createDialog ObjCopyPaste_Rollout
)

drdubosc
04-02-2009, 05:21 PM
...
process=dotnetclass "System.Diagnostics.Process"
process.start "http://www.dimensao3.com/al"
...



Well, I never knew you could do that. Mighty handy.

SoLiTuDe
04-02-2009, 06:42 PM
I stopped using INI stuff after all the limitations i encountered. Just in case, for those of you on max9/2008, the most recent Blur Beta tools include a component called BlurXML. It's very straightforward to use as well... guess it's about as easy as the .net stuff now that I look at it.


thedoc = blurxml.newDocument()
FileStuff= thedoc.addnode "FileStuff"
Pathinfo= FileStuff.addnode "Pathinfo"
Pathinfo.setattribute "Folder" (maxfilepath)
Pathinfo.setattribute "File" (maxfilename)
blurxml.savedocument thedoc (maxfilepath + "test.xml")


creates:
<?xml version="1.0" encoding="utf-8" ?>

<FileStuff>

<Pathinfo Folder="U:\2012\LAL\LAL_058_380\3D\LAL_058_380_fxEnv_GroundBreaking\" File="LAL_058_380_fxEnv_GroundBreaking_v055_015.max" />

</FileStuff>


and reading is just as straightforward... nodes have .children and .attributes and there are functions for more specific stuff too. Very simple example:

readdoc = blurxml.loaddocument (maxfilepath + "test.xml")
readdoc.children[2].children[1].attributes[1].value --returns "U:\2012\LAL\LAL_058_380\3D\LAL_058_380_fxEnv_GroundBreaking\"


--Thought I'd share! :D

CGTalk Moderation
04-02-2009, 06:42 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.