Notifications
Clear all

[Closed] help trying to modify a function

Hi all,

I’ve been following the revealing tutorial by max script by Louis Marcoux here I tried building on the tutorial by making an integrated UI for the script.

what the script do essentially is store the current transformation of a bunch of objects, then animate them transforming from an arbitrary position,rotation, or scale set by the user to the stored one consecutively.

the necessity arise to make some kind of sorting so I don’t have to precisely select hundreds of object with the same order I need to do my animation with, so Louis suggested the qsort function.


 qsort theOrderedArray CompareHeight

compareheight is a function that has been defined as follow


   fn CompareHeight TheObject1 TheObject2  = 
 			(
 				if (theObject1.pos.z == TheObject2.Pos.z) then 0
 				else
 				if (theObject1.pos.z < theObject2.pos.z ) then -1 else 1
 			)

the Problem with that is that the direction is always set and if I want to reverse the direction I have to open the script and modify the code, I want to be able to modify the code from the interface I’ve built.

I tried to introduce a third argument for the function and assign it a value of -1 depending on the corresponding checkbox checked but I couldn’t do it.

here’s the full script with my trials

macroscript  Powertransform category:"myScripts"
 (
 	Rollout Folder "Animation"
 	(
 		local range = [0,1000,0]
 		spinner AnimStart "Animation Start" range:range type:#integer
 		spinner AnimEnd "Animation End" range:range type:#integer
 		spinner dur "Animation Duration" range:range type:#integer
 		
 		group "Animation Parameters"
 		(
 		checkbox posCB "Position"
 		checkbox rotCb "Rotation"
 		checkbox scalCB "Scale"
 		checkbox visCB "Visibility"
 		)
 		group "Reverse Direction"
 		(
 		checkbox RevX "X"
 		checkbox RevY "Y"
 		checkbox RevZ "Z"
 		)
 		
 		group "Initial Position"
 		(
 			spinner Xposinit "X" range:[-100000,100000,0] type:#float
 			spinner Yposinit "Y" range:[-100000,100000,0] type:#float
 			spinner Zposinit "Z" range:[-100000,100000,0] type:#float
 		)		
 		group "Initial Rotation"
 		(
 			spinner Xrotinit "X" range:[-100000,100000,0] type:#float
 			spinner Yrotinit "Y" range:[-100000,100000,0] type:#float
 			spinner Zrotinit "Z" range:[-100000,100000,0] type:#float
 		)
 		group "Initial Scale"
 		(
 			spinner Xscalinit "X" range:[-100000,100000,100] type:#float
 			spinner Yscalinit "Y" range:[-100000,100000,100] type:#float
 			spinner Zscalinit "Z" range:[-100000,100000,100] type:#float
 		)
 	
 		button Exec "Create Animation"
 		button Dlt "Delete Animation"
 		
 		on Exec pressed do
 		(
 			if selection.count == 0 then
 			(
 				messageBox "Error:Selection set is empty!"
 			)
 			else
 			(
 			--Equation to figure out offsets
 			OffsetAnimation = ((AnimEnd.value - dur.value) - AnimStart.value)/ selection.count
 			Duration = dur.value
 			TheCurrentFrame = AnimStart.value
 			
 			TheOrderedArray = deepcopy (selection as array)
 			
 			a = 1
 			
 			if RevZ.checked do a = (-1) else (a = 1)
 			
 			fn CompareHeight TheObject1 TheObject2 a = 
 			(
 				if (theObject1.pos.z == TheObject2.Pos.z) then 0
 				else
 				if (theObject1.pos.z < theObject2.pos.z ) then -1*a else 1*a
 			)
 			
 			qsort TheOrderedArray (Compareheight a)
 
 			for a in TheOrderedArray do 
 			(
 				
 				
 				with animate on 
 				(
 				
 					if PosCB.checked do 
 						(
 							--position at end frame
 							at time (TheCurrentFrame + Duration) a.pos.x = a.pos.x
 							at time (TheCurrentFrame + Duration) a.pos.y = a.pos.y
 							at time (TheCurrentFrame + Duration) a.pos.z = a.pos.z
 							--position at start frame
 							at time (THeCurrentFrame) a.pos.x = a.pos.x  + Xposinit.value
 							at time (THeCurrentFrame) a.pos.y= a.pos.y + Yposinit.value
 							at time (THeCurrentFrame) a.pos.z = a.pos.z + Zposinit.value					
 							--position at frame 0
 							at time 0 a.pos.x = a.pos.x + Xposinit.value
 							at time 0 a.pos.y = a.pos.y + Yposinit.value
 							at time 0 a.pos.z = a.pos.z + Zposinit.value
 						)
 						
 					if rotCB.checked do
 						(					
 							--rotation at end frame
 							at time (TheCurrentFrame + Duration) in coordsys local a.rotation.x = 0.0
 							at time (TheCurrentFrame + Duration) in coordsys local a.rotation.y = 0.0
 							at time (TheCurrentFrame + Duration) in coordsys local a.rotation.z = 0.0
 							--rotation at start frame
 							at time (TheCurrentFrame) in coordsys local a.rotation.x = degtorad Xrotinit.value
 							at time (TheCurrentFrame) in coordsys local a.rotation.y = degtorad Yrotinit.value
 							at time (TheCurrentFrame) in coordsys local a.rotation.z = degtorad Zrotinit.value	
 							--rotation at frame 0
 							at time 0 in coordsys local a.rotation.x = degtorad Xrotinit.value
 							at time 0 in coordsys local a.rotation.y = degtorad Yrotinit.value
 							at time 0 in coordsys local a.rotation.z = degtorad Zrotinit.value
 						)		
 
 					if scalCB.checked do
 						(
 							a.scale.controller = scaleXYZ()
 							--scale at end frame
 							at time (TheCurrentFrame + Duration) a.scale.x =1.0
 							at time (TheCurrentFrame + Duration) a.scale.y =1.0
 							at time (TheCurrentFrame + Duration) a.scale.z =1.0
 							--scale at start frame
 							at time (TheCurrentFrame) a.scale.x =  Xscalinit.value/100
 							at time (TheCurrentFrame) a.scale.y =  Yscalinit.value/100
 							at time (TheCurrentFrame) a.scale.z =  Zscalinit.value/100
 							--scale at frame 0
 							at time 0 a.scale.x = Xscalinit.value /100
 							at time 0 a.scale.y = Yscalinit.value /100
 							at time 0 a.scale.z = Zscalinit.value /100
 						)
 						
 					if visCB.checked == true do
 						(
 							a.visibility = bezier_float()
 							--visibility at end frame
 							at time (TheCurrentFrame + 1.0 ) a.visibility.controller.value = 1.0
 							--visibility at start frame
 							at time (TheCurrentFrame)  a.visibility.controller.value = 0.0
 							--visibility at frame 0
 							at time 0 a.visibility.controller.value = 0.0
 						 )
 				)
 					theCurrentFrame = TheCurrentFrame + OffsetAnimation
 			)
 			)
 		)
 		on Dlt pressed do 
 		(
 			macros.run "Animation Tools" "DeleteSelectedAnimation"
 		)
 	)
 	createdialog Folder
 )

my questions are
1-How can I achieve this?
2-The visibility sometimes goes wacko if I’m not on frame 0. what’s causing this?
3-I think there’s a better way to consolidate the script a bit, so any tips would be appreciated.

thanks.

10 Replies

OK. let’s do it step by step

  1. qsort function:

qsort function needs pointer to the comparison function (see mxs help for qsort). The comparison function can have keyword arguments.


 fn compByHeight n1 n2 reverse:off = 
 (
 	   a = if n1.pos.z < n2.pos.z then -1 else  if n1.pos.z > n2.pos.z then 1 else 0
 	   if reverse then -a else a
 )
 qsort nodelist compByHeight reverse:on
 

TheOrderedArray = deepcopy (selection as array)

you don’t need to make deep copy of selection array because (selection as array) returns a copy array of selection (objectset) (see mxs help for getCurrentSelection())

  1. (optimization)

   at time (TheCurrentFrame + Duration) a.pos.x = a.pos.x
   at time (TheCurrentFrame + Duration) a.pos.y = a.pos.y
   at time (TheCurrentFrame + Duration) a.pos.z = a.pos.z
   
first of all it doesn't make any sense. you are setting same position at frame (TheCurrentFrame + Duration) that currently set at this frame. 

if you want to set position at frame A from frame B you have to use:


   at time A n.pos = at time B n.pos
   

you are using the same frame – (TheCurrentFrame + Duration) in many places. It’s better to assing this value once to some local variable and use this variable in calculations.


   a = [0,0,0]
   b = [1,2,3]
   -- pointer3 operations:
   a.x += b.x
   a.y += b.y
   a.z += b.z  
   -- use the optimal way:
   a += b -- (same result but faster and shorter)
   

(see mxs help for Point3 Value)


   /*
   at time (TheCurrentFrame + Duration) in coordsys local a.rotation.x = 0.0
   at time (TheCurrentFrame + Duration) in coordsys local a.rotation.y = 0.0
   at time (TheCurrentFrame + Duration) in coordsys local a.rotation.z = 0.0
   */
   local t = TheCurrentFrame + Duration
   at time t in coordsys local a.rotation = eulerangles 0 0 0
   

3+:
if all operations have to be in the same coordinate system you can block them all into one context


  /*
  --rotation at end frame
  at time (TheCurrentFrame + Duration) in coordsys local a.rotation.x = 0.0
  at time (TheCurrentFrame + Duration) in coordsys local a.rotation.y = 0.0
  at time (TheCurrentFrame + Duration) in coordsys local a.rotation.z = 0.0
  --rotation at start frame
  at time (TheCurrentFrame) in coordsys local a.rotation.x = degtorad Xrotinit.value
  at time (TheCurrentFrame) in coordsys local a.rotation.y = degtorad Yrotinit.value
  at time (TheCurrentFrame) in coordsys local a.rotation.z = degtorad Zrotinit.value	
  --rotation at frame 0
  at time 0 in coordsys local a.rotation.x = degtorad Xrotinit.value
  at time 0 in coordsys local a.rotation.y = degtorad Yrotinit.value
  at time 0 in coordsys local a.rotation.z = degtorad Zrotinit.value
  */
  in coordsys local 
  (
   	--rotation at frame 0
 	at time 0 a.rotation.x = ang
 	--rotation at start frame
 	ang = eulerangles (degtorad Xrotinit.value) (degtorad Yrotinit.value) (degtorad Zrotinit.value)			 
 	at time (TheCurrentFrame) a.rotation = ang
 	--rotation at end frame
 	at time (TheCurrentFrame + Duration) a.rotation = eulerangles 0 0 0
  )
  -- it's better to do "at time" calculation in forward time order 
  

I can’t thank you enough for your detailed answer, now I’m gonna need time to process this and see what I can do.:bowdown:

regarding the first problem I’ve tried the code you’ve posted and it worked perfect, however I’d like to be able to reverse all the 3 axis so I’ve modified it to give me the ability but it didn’t work.

Here’s what I did

	fn CompareByValue n1 n2 ReverseX:Off  ReverseY:Off ReverseZ:Off =
 		(
 			if (n1.pos.z==n2.pos.z ) then
 			(
 				if (n1.pos.y==n2.pos.y)then
 				(
 					if (n1.pos.x ==n2.pos.x)then
 					(
 						0
 					)else
 					( if (n1.pos.x>n2.pos.x AND reverseX==off )then
 						(
 							1
 						)else(-1))
 				)else
 					(
 						if(n1.pos.y>n2.pos.y AND ReverseY==Off)then(1)else(-1)
 					)
 			) else
 				(
 					if(n1.pos.z<n2.pos.z AND ReverseZ==Off)then(1)else(-1)
 				)
 			
 		)
 			 
 			 revvX = Off
 			revvY = Off
 			revvZ = Off
 			
 			if revX.checked then revvX=on
 			if revY.checked then revvY=on
 			if revZ.checked then revvZ=on
 			
 			qsort TheOrderedArray CompareByValue reverseX:revvX reverseY:revvY reverseZ:revvZ

What am I doing wrong?

I suspect that the if statement is wrong, having the > condition & AND reverse isn’t encompassing all possibilities I guess, I’m not sure how to fix that though.

do you want to sort by point3 component? if so the comparison function has to look like:


fn copmByComponent v1 v2 c:1 reverse:off = 
(
	a = if v1[c] < v2[c] then -1 else if v1[c] > v2[c] then 1 else 0
	if reverse then -a else a
) 

for point3 values the “c” key argument has to be in range 1…3

That would allow me to compare either by X,Y or Z values but only one of them…right?

what I’d like to do is to make it first compare by z for instance, take the lowest z first unless reverse is on, if the 2 objects have the same Z value, it compares by y …and so on until it reaches x.

this is what the actual function in the tutorial did except it didn’t have a reverse option(s).

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

fn copmByComponent v1 v2 c:3 reverse:off = 
(
	if c = 0 then a = 0
	else a = if v1[c] < v2[c] then -1 else if v1[c] > v2[c] then 1 else
	( 
		copmByComponent v1 v2 c:(c-1) reverse:reverse
	)
	if reverse then -a else a
) 

I’ve tried that and got the following error

-- Compile error: No outer local variable references permitted here:  copmByComponent
 --  In line: 						copmByComponent v

can we use a function inside the same function definition?