PDA

View Full Version : Expression editor: numbers after point


Mickley
02-12-2003, 09:04 AM
I wrote an expression. Goal is to have my custom attribute to recieve a value of 3, when rotation of a joint would be Y=30 and Z=20. Or in other words Z has a value 2/3 of that of Y.
(for example: Y=3, Z=2... Y=6, Z=4... and so forth..)

Logically if it is one third larger/smaller it should be multiplied/divided by 1.5

Thats what I did:

if((joint.rotateZ == joint.rotateY/(1.5)) && (joint.rotateY != 0))
joint.rotate_ALL= 3;
else
joint.rotate_ALL= 0;

The problem here is that the expression has no effect whatsoever.
When I substitute 1.5 with 2 --works just fine.

What can possibly be wrong here? Why floating point numbers are not calculated?

rotate_ALL attribute was created as a Float and has min/default/max values of -30/0/30 respectively.

all this makes me afully mad cause I'm fighting with it for hours now. :mad: I admit that I'm may be doing something stupidly wrong here.
So please, people with mel experience or just with a fresh look. Heeeelp

mark_wilkins
02-12-2003, 09:32 AM
Well, I see what's going wrong, but I'm not sure what you were trying to do in the first place. :-)

The most basic problem is that testing whether two floating-point numbers are equal to each other is a problem. First, since floating-point numbers are usually used to represent continuously variable values, like rotation, it would be very odd that rotateY would EVER be EXACTLY 1.5 times the value of rotateZ. Even if your joint moves slowly through each axis, from one frame to the next (or one recalculation to the next, if you're moving the joint by hand with a manipulator) the ratio might jump from 1.499998 to 1.500001.

Furthermore, certain rational floating-point decimal numbers (rational meaning that they can be represented with a finite number of digits) are irrational in binary (which means that the computer cannot represent them exactly at all.) 1.5 is not such a number, but you could find examples where even if you SET joint.rotateY to be some multiple times joint.rotateX, if you immediately tested with the test you described it would NOT evaluate as equal.

The reason that your expression works when you divide by 2 is that, because 2 (as opposed to 2.0) is an integer and not a float, Maya converts EVERYTHING in the expression (including joint.rotateY and joint.rotateZ) to integers. This means that a range of Y and Z rotate values will result in the if statement being true, because Maya's shamelessly truncating and throwing away any part after the decimal point. So, if rotateY is 5.3, Maya evaluates rotateY/(2) as the integer division of 5 / 2, which is 2 (because the remainder gets thrown away.) Note that if rotateY is 4 or 4.3 or 4.8 or 5.9, all of these will result in that division being evaluated to 2, so a wide range of values can all come up "equal."

You have to make some choices. First, are you trying to do something special if the value of rotateY PASSES THROUGH 1.5 times the value of rotateZ? If so, you can test with > or < rather than == and determine which side of the transition you're on and change the value of joint.rotate_ALL from 3 to 0 or vice versa as appropriate.

Or, alternatively, are you trying to determine whether rotateY is within a narrow range around 1.5 times joint.rotateZ? In this case, you have to figure out how precise you want it to be, and then do something like:

float $epsilon = 0.01;
if ((joint.rotateZ < (joint.rotateY + $epsilon) / (1.5)) &&
(joint.rotateZ > (joint.rotateY - $epsilon) / (1.5)))

Then, whatever's in the if statement will be executed whenever your joint.rotateY is within 0.01 of the value you're looking for.

(note: "epsilon" is a variable name that's often used in math and computer science for how precisely you want to compare two floating-point values. You could call it $precision or something like that instead, but old habits die hard, I guess. :D.)

Floating-point comparisons like this are a very difficult issue for many programmers, and those who know this stuff have been bitten by these problems over and over and over again, so don't feel bad.

-- Mark

luminis
02-13-2003, 03:02 AM
Don't know if you have found a solution yet and not sure if this will work, but it's worth a try:

Change:
if((joint.rotateZ == joint.rotateY/(1.5)) && (joint.rotateY != 0))

To:
if((joint.rotateZ == 2*joint.rotateY/3) && (joint.rotateY != 0))

Change the divided by 1.5 to a multply by 2 and divide by 3 (same thing of course).

mark_wilkins
02-13-2003, 03:19 AM
Luminis:

All your suggestion does is force the computation to be performed in integer space, which creates an unintended and arbitrary lack of precision in the calculation.

Depending on the value of joint.rotateZ, this can produce very different results by percentage of the attribute's value. For example, if rotateZ has a value of 2, then rotateY could be any value from 3 to 4.9999 and the expression could come up looking "equal."

On the other hand, if rotateZ has a value of 180, rotateY could have any value from 270 to 271.9999 and the expression would come up equal.

In these two cases, the exact equality for which the original poster was searching happens at the bottom end of the range. Wouldn't one normally want whatever their expression were doing to happen when the value's in a range centered on some meaningful value rather than bracketed by that value on the lower end?

Also, who says that a range of two degrees is an acceptable precision for the test for equality? By not explicitly thinking that through, you're letting Maya's arithmetic decide that for you, with (depending on your application) God knows what possible effect.

Do it right and test in floating-point space, so that you and anyone else who cares will be able to read and understand your code rather than having to puzzle out some weird float-to-int side effect. :D

-- Mark

Mickley
02-13-2003, 01:40 PM
WOW.WOW.WOW!
Guys this is cool!

Mark I want to tell you. What a thorogh explnation!
Sorry didn't have a chance to get there past 24 hours.
Will be trying tonight.
I suspected that some integer converting stuff going on. Its just that I would not find a solution myself for a next couple of years:). As my experience in a field is quite shallow (yet) I have to employ method of "bumping-solvig problem" not "forseeing-avoiding".
Thank you very much for you answer I think that will work. I will post a result. Also explanation what I waned to do in a first place. Now I gotta run.

Luminus I was trying that method of cheating with integers. Unfortunately it didnt work either, that's why I posted the question here. But thank you for the try.

Mickley
02-14-2003, 06:09 AM
YAY! tHIS RALLY WORKED!!
Mark you are the best!:applause:
(I mean it)

Now what was the problem (and may be still is).

Joint01 (lets say shoulder) hooked up to blendshapes via set driven keys (for a nice deformation in extremes) Z is a UP/DOWN movement Y - FORWARD/BACK.
Blenshapes keyed at (0,0,0) rotation of the joint and each at respective (0, EndValue, EndValue).

Everything looks good until the shoulder is rotated in extreme YZ, Y-Z, -YZ, -Y-Z positions. I've added additional blenshapes for each of this positions. The problem arouse how to drive them via single attribute, as they actually should kick in only as a result of two rotations. The rest you know.:shrug:

BTW Just received your book today. Expecting quite a reading.:airguitar

Mickley
02-14-2003, 06:49 AM
Ok my celebration was a bit premature.

if(((shoulder_L.rotateZ < (shoulder_L.rotateY + $epsilon) / (1.5)) && (shoulder_L.rotateZ > (shoulder_L.rotateY - $epsilon) / (1.5)) && (shoulder_L.rotateY != 0)))
shoulder_L.rotate_YZ= (shoulder_L.rotateZ + shoulder_L.rotateY)/2;
else
shoulder_L.rotate_YZ= 0;

All this works like it supose to. But you were right. This kicks in when Z and Y are 2 to 3. This makes deformation jerky, as it throws in another blenshape suddenly when the conditions met. What about a whole range of motion between Y and Z?
Will be trying to figure it out. But if you can give me some advice on how to implement it the best way, before I broke my head over this, I will really appreciate it.

here is the pic
Joint01(on pic) and shoulder_L(in expression) is the same person:)
This is a -YZ quarter of rotation of Joint01. Blue numbers are extreme rotation values at -Y and Z.
Purple numbers illustrating how Rotate_YZ atribute changes when -Y and Z change.

mark_wilkins
02-14-2003, 07:17 AM
You don't have to have all the shapes as part of the same blend shape node. You can make multiple blend shape nodes and combine their effects:

1) Pick the extremes along one axis, then the target

2) Choose Deform > Create Blend Shape

3) Pick the extremes along the other axis, then the target

4) Choose Deform > Create Blend Shape

Now, you can use Y rotation with set driven keys to drive one of these blend shape nodes, and Z to drive the other.

Or you could make FOUR blend shape nodes, and use an expression (or better yet a network of multiplyDivide and plusMinusAverage nodes, for speed) to look at BOTH Y and Z to calculate the contribution of each of the four blend shapes.

Alternatively, you could possibly set up multiple layers of driven key nodes... not sure how that would quite work, but there might be a way to do it...

-- Mark

CGTalk Moderation
01-14-2006, 10:00 AM
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.