Add or subtract value to selected animation curves


#1

I’m basically trying to find the Max equivalent of Maya’s animation offset command ““+=360” or “-=360”” that allows you to add or subtract a numerical value to your current key selection. Does anyone know if 3dsMax has this function? If not, does anyone know of a current simple script that can do this?

Thanks!


#2

image

if you want to change a key value you have to be in animation mode and at the time of this key.
there is no UI solution to change multiple keys at the same moment


#3

What are you doing, trying to achieve - plain english?
When someone mention animation (keys ) and offsetting I automatically think about changing/offseting time values for anim. keys
And when I see +/- 360 than bout trans. rotation
And “numerical value to your current key selection” isn’t very precise term, or?


#4

What if you change value in non animation mode :slight_smile:


#5

For instance: say you have 3 keys selected in the Max curve editor with values “20, 40, and 60”. I want to edit all the of them at once “numerically” (as opposed to just manually dragging them in the curve editor). So let’s say I want to add +100 to all 3 keys. In Maya, you simply go into the curve editor, select the desired keys and enter “+=100” which would change their values to “120, 140, and 160”.

Obviously this is easy enough to do manually with just 3 keys. But when you’re dealing with hundreds, or even thousands of keys at once, doing this manually isn’t practical. I’m basically asking if Max has a “+=” or “-=” equivalent?


#6

Yeah I tried this as well. Unfortunately, this only works for keys on the current frame :confused:


#8
try (destroydialog OffsetSelectedKeysRol) catch()
rollout OffsetSelectedKeysRol "Offset Selected Keys" width:200
(
	fn offsetControllerValue offset:1.0 = 
	(
		if (tv = trackviews.current) != undefined do
		(
			num_keys = 0
			done = #()
			for k=1 to tv.numSelTracks() do 
			(
				c = tv.getSelected k
				if (iskindof c FloatController) and (numKeys c > -1) and (findItem done c == 0) do
				(
					append done c
					if (num = numSelKeys c) > 0 do
					(
						keys = for k in c.keys where k.selected collect k
						keys.value += offset
						num_keys += num
					)
				)
			)
			#(done, num_keys)
		)
	)

	edittext exprassion_ed " Execute Expression: " labelOnTop:on fieldwidth:120 align:#left offset:[-4,6] across:2
	edittext lastoffset_ed " Value: " labelOnTop:on readonly:on fieldwidth:60 align:#right offset:[4,6]
	
	label emp offset:[0,-10]
	
	on exprassion_ed entered text do
	(
		v = try(execute text) catch()
		if iskindof v Number then undo "Key Value Offset" on  
		(
			offsetControllerValue offset:(v as float)
			exprassion_ed.text = ""
			lastoffset_ed.text = v as string
		)
		else
		(
			lastoffset_ed.text = ""
		)
	)
)
createdialog OffsetSelectedKeysRol

of course it’s possible to make a macros with nice interface but it’s for another enthusiasts…
how it woks…
open any trackview and select controllers you want to work with.
write any expression that make sense and does make a valid number and enter the text.
the executed expression makes the value that will be added to all selected keys to selected controllers

simple case expression:
10 (adds 10)
-10 (subtract 10)


#9

it’s easy to extend the script to do more fancy calculations :slight_smile:
as well as support spinners or sliders. but again, it’s for how wants to practice in MXS


#10

also i remember that i posted example of a FANCY expression dialog on this forum but i don’t know now how to find it with new forum’s UI

oh! i found it max calculator help
:slight_smile:


#11

This is awesome! Thanks so much. I’ll give this a try


#12

This is perfect!!! Thank a million!


#13

One last thing, it seems that it’s using radians instead of degrees for rotational values. Is there an easy way to adjust that?


#14

you can use any mxs expression …
in the rotation value case it’s:
degtorad 10

10 degrees are converted to radians


#15

I was inspired by your code denisT. This acts more like the Maya edit boxes.

  • Edit time on the left, edit value on the right
  • Just putting in a number is an absolute time or value
  • Using ?=X where ? is an operator and X is a number will offset the value by the operator. (examples: +=10, *=5, /=2, -=1)

Warning there is barely any error checking in the code below.

To do (but probably never will. :smiley: )

  • Selection callback to update the edit boxes based on the selected keys.
  • Always use degrees for rotation controllers
  • Make it dockable in the Curve Editor
try (destroydialog OffsetSelectedKeysRol) catch()
rollout OffsetSelectedKeysRol "Offset Selected Keys" width:300 height:28
(
    fn OffsetSelectedKeys pointer =
    (
        if (tv = trackviews.current) != undefined do
        (
            num_keys = 0
            done = #()
            numSelTracks = tv.numSelTracks()
            for k=1 to numSelTracks do 
            (
                c = tv.getSelected k

                if (iskindof c FloatController) and (numKeys c > -1) and (findItem done c == 0) do
                (
                    append done c
                    if (num = numSelKeys c) > 0 do
                    (
                        keys = for k in c.keys where k.selected collect k

                        pointer keys
                        sortKeys c
                        num_keys += num
                    )
                )
            )
            #(done, num_keys)
        )
    )
    
    fn changeKeys setting text control =
    (
        control.text = ""
        
        offset = undefined
        operator = ""
    
        expr = filterString text "="
    
        if expr.count == 1 do offset = expr[1]

        if expr.count == 2 do
        (
            offset = expr[2]
            operator = expr[1]
        )
        
        if offset != undefined do
        (
            customFunction = "mapped fn applyOffsetToKeys keys = (keys." + setting + " " + operator + "= " + offset + ")"
            try(execute customFunction)catch()

            undo "Key Time Offset" on  
            (
                OffsetSelectedKeys applyOffsetToKeys
            )
        )
    )

    edittext edt_time "T: " width:120 align:#left across:2
    edittext edt_offset "V: " width:120 align:#left
    
    on edt_time changed text do ()
    on edt_offset changed text do ()
    
    on edt_offset entered text do changeKeys "value" text edt_offset
    on edt_time entered text do changeKeys "time" text edt_time
)
createDialog OffsetSelectedKeysRol

#16

This is fantastic! An automatic radToDegree operator would be great addition for sure


#17

So I’ve been using this little script for my project throughout the last week. It’s made my life a million times easier. Thanks a million for the help. Honestly, I’m pretty mystified that Max never added a built-in function for this


#18

Here it is with rotation support. The real head scratcher is Point3 controllers. If you have X key selected all YZ also return that they are selected.

try (destroydialog OffsetSelectedKeysRol) catch()
rollout OffsetSelectedKeysRol "Offset Selected Keys" width:300 height:28
(
    fn OffsetSelectedKeys pointer =
    (
        if (tv = trackviews.current) != undefined do
        (
            num_keys = 0
            done = #()
            numSelTracks = tv.numSelTracks()
            for k=1 to numSelTracks do 
            (
                c = tv.getSelected k
				
				isRotation = if superclassof (tv.getParent (tv.getIndex c)) == rotationController then true else false

                if (iskindof c FloatController) and (numKeys c > -1) and (findItem done c == 0) do
                (
                    append done c
                    if (num = numSelKeys c) > 0 do
                    (
                        keys = for k in c.keys where k.selected collect k

                        pointer keys isRotation
                        sortKeys c
                        num_keys += num
                    )
                )
            )
            #(done, num_keys)
        )
    )
    
    fn changeKeys setting text control =
    (
        control.text = ""
        
        offset = undefined
        operator = ""
    
        expr = filterString text "="
    
        if expr.count == 1 do offset = expr[1]

        if expr.count == 2 do
        (
            offset = expr[2]
            operator = expr[1]
        )
        
        if offset != undefined do
        (
            customFunction = "mapped fn applyOffsetToKeys keys isRotation = (offset = if isRotation then degtorad " + offset + " else " + offset +"; keys." + setting + " " + operator + "= offset)"
            try(execute customFunction)catch(messagebox "compile failed.")

            undo "Key Time Offset" on  
            (
                OffsetSelectedKeys applyOffsetToKeys
            )
        )
    )

    edittext edt_time "T: " width:120 align:#left across:2
    edittext edt_offset "V: " width:120 align:#left
    
    on edt_time changed text do ()
    on edt_offset changed text do ()
    
    on edt_offset entered text do changeKeys "value" text edt_offset
    on edt_time entered text do changeKeys "time" text edt_time
)
createDialog OffsetSelectedKeysRol

#19

what does it mean?

Edited

Oh! I see now… Probably “pointer” is not clear name for this argument


#20

So I just tested this out. It seems that the time offset-er is shifting the frames at the same 6.28/360 (1 / 57th) ratio as degToRad so now it’s offsetting animation frames by a much smaller amount.


#21

@denisT Sorry for the confusion. I asked a programmer at work what a function passed as a variable was called and he said a Pointer. But doesn’t matter because execute is in global scope. I don’t need to pass it as a variable, so I just renamed it to the function name.

@LordRaidis Sorry! It’s an obvious bug after you mentioned it. I can fix it, but alas our work was in vain. The default “Key Stats” toolbar is dumb and nearly useless. The hidden “Key Entry” toolbar does exactly this functionality. The syntax is a little different than Maya. See the following page.