Notifications
Clear all

[Closed] 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?

13 Replies

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…

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.:shrug:

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
)

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.

1 Reply
(@lonerobot)
Joined: 11 months ago

Posts: 0

Not at all!

nice solutions, people.

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	
)

Sometimes, I love this forum … K.I.S.S.

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.

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

Page 1 / 2