Copy/Paste transform data?

Become a member of the CGSociety

Connect, Share, and Learn with our Large Growing CG Art Community. It's Free!

THREAD CLOSED
 
Thread Tools Search this Thread Display Modes
  04 April 2009
Copy/Paste transform data?

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?
 
  04 April 2009
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...
__________________
Bobo
 
  04 April 2009
UI's always take forever for this sort of thing. I am currently using XML to to store transforms and using this 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.
__________________
Regards,
Pete

Read my technical blog - lonerobot.net
 
  04 April 2009
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
)
__________________
Bobo
 
  04 April 2009
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.
__________________
Regards,
Pete

Read my technical blog - lonerobot.net
 
  04 April 2009
Originally Posted by LoneRobot: 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.


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.
__________________
Bobo
 
  04 April 2009
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	
)
__________________
Artur Leao | Co-Founder / Project Manager
You can do it! VFX
Porto/Lisbon - Portugal
http://www.ycdivfx.com
 
  04 April 2009
Originally Posted by Bobo: Hope I did not offend.


Not at all!

nice solutions, people.
__________________
Regards,
Pete

Read my technical blog - lonerobot.net
 
  04 April 2009
Sometimes, I love this forum .... K.I.S.S.
 
  04 April 2009
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.
 
  04 April 2009
I guess I've found some limitation in the getIniSetting, I can't get past 1175 items Anyone knows something about this?

Edit: And using Bobo structure I can only get 1162 items :/
__________________
Artur Leao | Co-Founder / Project Manager
You can do it! VFX
Porto/Lisbon - Portugal
http://www.ycdivfx.com

Last edited by Kameleon : 04 April 2009 at 04:54 PM.
 
  04 April 2009
OK, kind of solved it... did the XML version 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
)
__________________
Artur Leao | Co-Founder / Project Manager
You can do it! VFX
Porto/Lisbon - Portugal
http://www.ycdivfx.com
 
  04 April 2009
Originally Posted by Kameleon:

 ...
   process=dotnetclass "System.Diagnostics.Process"
process.start "http://www.dimensao3.com/al"
  ...
  



Well, I never knew you could do that. Mighty handy.
 
  04 April 2009
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_Groun  dBreaking\" 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_Groun  dBreaking\"
  


--Thought I'd share!
__________________
http://www.fx-td.com
 
  04 April 2009
Thread automatically closed

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.
__________________
CGTalk Policy/Legalities
Note that as CGTalk Members, you agree to the terms and conditions of using this website.
 
Thread Closed share thread



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
CGSociety
Society of Digital Artists
www.cgsociety.org

Powered by vBulletin
Copyright 2000 - 2006,
Jelsoft Enterprises Ltd.
Minimize Ads
Forum Jump
Miscellaneous

All times are GMT. The time now is 11:22 PM.


Powered by vBulletin
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.