Notifications
Clear all

[Closed] curveControl using the wrong values

Hey guys,

 A little while ago I was trying to mathematically convert the tangent handle values of a Bezier key in the Track View to point2 values so I could reproduce the curve in Max's curveControl UI element ([back in this thread]( http://forums.cgsociety.org/showthread.php?f=98&t=986723))  
 
 Well I got the math figured out and was able to find the correct values for the curveControl UI. The problem is that even though I'm giving the curveControl the right values... it's refusing to use them.
 
 [img] http://dl.dropbox.com/u/3850172/Control-O-Tron/curveControl%20problem.jpg [/img]
[]( http://dl.dropbox.com/u/3850172/Control-O-Tron/curveControl%20test.max) [Here is a link to the Max scene.]( http://dl.dropbox.com/u/3850172/Control-O-Tron/curveControl%20test.max)  (Made in Max 9, should work on anything 9 and later)

[Here is a link to the script.]( http://dl.dropbox.com/u/3850172/Control-O-Tron/bezier_to_curveControl.ms) 

First I apologize for the script. It's just a test right now so I think my code is probably pretty embarrassing. Most of it is the just the curveControl script from the help file, then I added in some stuff to grab values off the Bezier curve. 

Anyway, open curveControl test.max and select the yellow Test Box then evaluate the script. The curveControl pops up with three curves. These curves represent Bezier Float controllers in channels 2, 3 and 4 of the Morpher Modifier in the yellow Test Box's stack. These channels each have List Controllers with a Script Controller and a Bezier Float named "Curve". The controllers named "Curve" are what the curves in the curveControl are supposed to look like. I have all the outTangent values printing out to the Listener right now the first time you evaluate the script. Then if you press the Print Properties button on the curveControl it'll print the values that it's actually using in the Listener.

Can anybody help me figure out why the values keep coming out different than the ones that I give it to use?
19 Replies

there is no one-to-one correspondence between track view and curve control bezier curves. their tangents make different sense. what do you want to get? trackVeiw to curveControl or control to trackView?

Hey!
I am not sure about the intangent and outtangent, but you should use insertPoint function instead of specifying the numpoints and then using a loop to add new points (because you are trying to add points to an array which has a size of 2 IMO! so even if you hardcode the values for intangent and outtangent the curve control will show wrong values!) i.e the problem is in this bit of the code :


   crv.numPoints = (numKeys ($.modifiers[#morpher][findMorphListCont[i]].curve.controller))		
   for j=1 to crv.numPoints do(
   

But it works if you use insertpoint function instead, only you have to figure out how to convert the intangents and outtangents from curve editor to curve control ! (i will be needing this too for a project, i wanted to write script to copy the controllers from loft deformations to certain property of an object (which is in another max file!).
however i cant figure out how max stores the in/out Tangents, For example, for a 2 point curve as in the image below max stores the outtangent for the first point as 0.1875 :S instead of 1 (i always thought it was storing the arc tangent, but something else is going on here :S)


   local findMorphListCont = (
   	numTargets = 1
   	theMorpher = $.modifiers[#morpher]
   	while WM3_MC_Hastarget theMorpher numTargets do numTargets = numTargets + 1
   	for i = 2 to numTargets where ( ((classof theMorpher[i].controller) == float_list) and 
 													 (hasproperty theMorpher[i].controller #curve) ) collect i 
   )
   local findMorphCurves = (
   	for i = findMorphListCont collect (
   		for j in 1 to findMorphListCont.count where $.modifiers[#morpher][i][j].name == "curve" collect j
   	)
   )		
   fn getKeyValue theKey theFrameCount theNewFrameCount timeRange = (				
   	theXValue = ((theKey.time.frame - timeRange.start) / theFrameCount) * theNewFrameCount
   	theYValue = theKey.value						
   	return [theXValue,theYValue]
   )
   

   on uTestCurveControl open do(			
   	zoom cc_test #all		
   	local colors = #(red, green, blue)		
   	local styles = #(#solid, #dash, #dot, #dashDot, #dashDotDot, #null, #insideFrame)		
   	local num = cc_test.numCurves 		
   	
   	for i = 1 to num do (
   		theCurve = cc_test.curves[i]
   		theCurveTimeRange = getTimeRange $.modifiers[#morpher][findMorphListCont[i]].curve.controller
   		
   		theFrameCount = abs(theCurveTimeRange.start.frame - theCurveTimeRange.end.frame)
   		theNewFrameCount = abs(cc_test.x_range.x - cc_test.x_range.y)
   			
   		numberOfKeys = numkeys ($.modifiers[#morpher][findMorphListCont[i]].curve.controller)
   		theFirstKey = getkey $.modifiers[#morpher][findMorphListCont[i]].curve.controller 1
   		theLastKey = getkey $.modifiers[#morpher][findMorphListCont[i]].curve.controller numberOfKeys
   		
   		theFirstPoint = ccPoint (getKeyValue theFirstKey theFrameCount theNewFrameCount theCurveTimeRange) (getIT theFirstKey theScaleFactor) (getOT theFirstKey) Bezier:true corner:true
   		theLastPoint = ccPoint (getKeyValue theLastKey theFrameCount theNewFrameCount theCurveTimeRange) (getIT theLastKey theScaleFactor) (getOT theLastKey) Bezier:true corner:true
   		
   		setPoint theCurve 1 theFirstPoint
   		setPoint theCurve 2 theLastPoint
   		
   		for j = 2 to (numberOfKeys - 1) do (
   			theKey = getkey $.modifiers[#morpher][findMorphListCont[i]].curve.controller j
   			theKeyValue = (getKeyValue theKey theFrameCount theNewFrameCount theCurveTimeRange)
   			curvePoint = ccPoint (getKeyValue theKey theFrameCount theNewFrameCount theCurveTimeRange) (getIT theKey) (getOT theKey) Bezier:true corner:true
   			insertPoint theCurve j curvePoint
   		)
   	)
   

Haha, my super sad face worked!

@Furball89
Oh no, you’re right! That outTangent is 45 degrees, its value should be 1… but it’s 1.875. That kind of changes everything. Now I have no idea how to convert those values over to a point2.

One question on your code:

theFirstPoint = ccPoint (getKeyValue theFirstKey theFrameCount theNewFrameCount theCurveTimeRange) (getIT theFirstKey theScaleFactor) (getOT theFirstKey) Bezier:true corner:true
  			  theLastPoint = ccPoint (getKeyValue theLastKey theFrameCount theNewFrameCount theCurveTimeRange) (getIT theLastKey theScaleFactor) (getOT theLastKey) Bezier:true corner:true
  

I think getIT and getOT are referring to functions but I don’t see them anywhere in the code. Also, theScaleFactor doesn’t seem to be defined either. Am I missing something? Can I just use the formula I was using in the original code for these functions?

By the way, thank you so much for your code. It is a vast improvement over my own :bowdown:

@DenisT
I’m trying to get trackVeiw to curveControl. I thought I had a formula worked out that would convert the TrackView curve’s .inTangent and .inTangentLength to an [x,y] point2 value I could use for the curveControl in/out tangents, but like Furball pointed out it looks like those numbers aren’t what I thought they were.

Thanks for all the help so far!

Hm yea, thats the problem bit, i can’t figure out the getIT and getOT functions yet! (the code i posted is able to insertpoints in the curve (for eg, if you hard code [-10,-10] and [10,10] for in / out tangent respectively it will create the tangents in the curve control, but if you do the same with the previous code it won’t create the outtangent for some points, as shown in the figure you posted earlier, so i re-scripted it using the maxscript helpfile!) my getIT and getOT functions are flawed (they only work if the time range is not scaled), sadly, the problem is, if you scale the timerange then both x and y coordinates of the in/out tangents will change (in the curve editor) the new x coordinate is easy to find, i think your code finds that bit correctly, but the y coordinate is the problem! unless you can somehow figure out what the in/out tangent values mean! i tried every possible way to look at this but 0.1875 makes no sense! also you cannot have and in/out tangent weight of more than 1 in the curve control (bound by the previous key, ofcourse), where as in the curve editor you can drag the handle ahead of the next key for infinity!

You’re right about the tangents in the curveControl being constrained between X 0 and X 100. It’s kind of a pain but I could live with that limitation.

Also, I’m messing around in the Track View right now too, trying to figure out if an outTangent of 1.00 is ever equal to 45 degrees and I can’t figure it out either… so weird.

I will say this, the curveControl is remarkably hard to work with. I kind of think I hate it. For rigging and animation though curves are SO important. I wish Max had a more convenient way to incorporate curves in to scripted tools and UI

you could however compute the tangents yourself using the controller’s value at the next or previous frame, some thing along the lines :-


fn remapKey theKey timeRange = (		
	theOldFrameCount = abs(timeRange.start.frame - timeRange.end.frame)
	--theNewFrameCount = abs(cc_test.x_range.x - cc_test.x_range.y)
	theNewFrameCount = 100
	theXValue = ((theKey.time.frame - timeRange.start.frame) / theOldFrameCount) * theNewFrameCount
	theYValue = theKey.value						
	return [theXValue,theYValue]							
)	
fn getIT theController previousKey thisKey timeRange = (				
	remappedPreviousKey = remapKey previousKey timeRange
	remappedThisKey = remapKey thisKey timeRange				
	
	newFrameCount = abs(remappedThisKey.x - remappedPreviousKey.x)					
	theXValue = thisKey.inTangentLength * newFrameCount					
	
	thisValue = at time (thisKey.time) theController.value
	previousValue  = at time (thisKey.time - 1) theController.value					
	
	theYValue = theXValue * (previousValue - thisValue)					
	
	return [-theXValue,theYValue]	
)
fn getOT theController thisKey nextKey timeRange= (				
	remappedThisKey = remapKey thisKey timeRange				
	remappedNextKey = remapKey nextKey timeRange
	
	newFrameCount = abs(remappedThisKey.x - remappedNextKey.x)					
	theXValue = thisKey.outTangentLength * newFrameCount					
	
	thisValue = at time (thisKey.time) theController.value
	nextValue = at time (thisKey.time + 1) theController.value					
	
	theYValue = theXValue * (nextValue - thisValue)					
	
	return [theXValue,theYValue]
)			
fn getCurvePoint theController keyIndex timeRange = (
	if ((keyIndex - 1) > 0) then
		previousKey = getkey theController (keyIndex - 1)				
	else
		previousKey = -1
	
	format "PreviousKey = %
" PreviousKey
	
	thisKey = getkey theController keyIndex
	
	if ((keyIndex + 1) <= (numkeys theController)) then
		nextKey = getkey theController (keyIndex + 1)
	else
		nextKey = -1 
				
	thisKey = getkey theController keyIndex				
	remappedKey = remapKey thisKey timeRange
	
	IT = [0,0]
	OT = [0,0]
	if (previousKey != -1) then					
		IT = (getIT theController previousKey thisKey timeRange)
	
	if (nextKey != -1) then					
		OT = (getOT theController thisKey nextKey timeRange) 
	
	return (ccPoint remappedKey IT OT Bezier:true corner:true)
)
for i = 1 to num do (
	theCurve = cc_test.curves[i]
	theController =  $.modifiers[#morpher][findMorphListCont[i]].curve.controller
	theCurveTimeRange = getTimeRange theController
	
	theOldFrameCount = abs(theCurveTimeRange.start.frame - theCurveTimeRange.end.frame)
	theNewFrameCount = abs(cc_test.x_range.x - cc_test.x_range.y)

	numberOfKeys = numkeys ($.modifiers[#morpher][findMorphListCont[i]].curve.controller)																
	
	setPoint theCurve 1 (getCurvePoint theController 1 theCurveTimeRange)
	setPoint theCurve 2 (getCurvePoint theController numberOfKeys theCurveTimeRange)
	
	for j = 2 to (numberOfKeys - 1) do 
		insertPoint theCurve j (getCurvePoint theController j theCurveTimeRange)
) 
 

still some bug with scaling, works with non scaled curve though!

I know that mxs help doesn’t explain well how in/out tangents of track view and curve control bezier curves work.
So I’ll try to clear it up.

[b]Track View curve (tvc):[/b]
inTangent and inTangentLength define in-tangent handle position
outTangent and outTangentLength define out-tangent

inTangentLength(outTangentLength) is a time distance of handle from current key to previous(next) key. Full distance is 1.0, half 0.5...
So if the distance 16 frames and handle on half way to the adjacent key the inTangentLength is 0.5, which corresponds to (current_key_time - 16*0.5) handle X position.
in-tangent handle Y position is[b] ( inTangent*inTangentLength )

[/b]
Curve Control curve (ccc):
inTangent and outTangent are poitn2 values. The values in curveControl units, they are relative positions to point value.
So if point’s value [0.5, 0.5] and its in-tangent value is [-0.1, 0.1], the in-tangent handle position is [0.5, 0.5] + [-0.1, 0.1] = [0.4, 0.6] ( value + inTangent )

How to translate ccc to tvc?
current ccc point is ([b]value[/b])
inTangent of ccc point is ([b]inTangent[/b])
previous ccc point is ([b]prev_value[/b])

inTangentLength of tvc point equals[b] ( inTangent.x / (value.x - prev_value.x)[/b] )
inTangent equals[b] ([/b] [b]inTangent.y / inTangentLength )

EDIT:
i didn’t have max open at the moment when i was writing the post. There are some mistakes that I made. But I’m to lazy today to fix them. The main idea is right. The snippet below proves it

[/b]

in-tangent handle Y position is ( inTangent*inTangentLength )

@ denisT:
Can you please explain this with an example , it would be great if you could explain why the outtangent for 45 degree angle is 0.1875 (see the image in my previous post) ? i.e. :


   (
   	x = 0.5 [color=Lime]--outTangentLength[/color]
   	y = x * 0.1875 --outTangent * outTangentLength
   	print (atan2 y x) --output is [b]10.6197[/b] !?!?!
   )
   

Thanks

Page 1 / 2