Pregunta

I am using box2d engine in c++ and I let some images float freely on the screen. The wander but they also rotate (spin) endlessly around their centre of mass - as if they are in space.

Once in a while, a trigger is set and I want to assume command of the spinning to bring it to a stop. Also I do not want to do that immediately but rather continue the rotation until the image is in the upright position.

Now here is the catch, the box2d physics engine keeps track of the total rotational distance that has been spinned around. That is if the image (for example) has spinned 5 times , the getRotation() member function will return a value (in rads) between 10*pi < value < 12*pi and not a value between 0 < value < 2*pi .

However, I do not want to unwind all the logged rotations , just stop at the nearest 2*PI multiple. As such, some modulus (%) operation should be in order. I can not use % because the denominator of this division should be pi (i.e not an integer). I looked into fmodf but I was not able to get the expected result:

if(( abs( fmodf(box2dObj.getRotation() , 2*pi) ) == 0 ) ||
   ( abs( box2dObj.getRotation() < STEP ) )
     box2dObj.setRotation( 0 ) ; //remain at 0
else if ( fmodf(box2dObj.getRotation() , pi) ) > 0 )
     box2dObj.setRotation( --box2dObj.getRotation() ) ; //decrease towards 0 if 0 < x < pi
else if ( fmodf(box2dObj.getRotation() , pi) ) < 0 )
     box2dObj.setRotation( ++box2dObj.getRotation() ) ; //increase towards 0 if pi < x < 2*pi

STEP is defined as the increment / decrement step size . here is it one (but I have tried with much smaller increment / decrement step sizes, not just ++ and -- which default to a step of 1).

would become zero even at times when the image was not in the upright position.

what can I do to find when the rotation is at a multiple of 2*pi ?

¿Fue útil?

Solución

Firstly, you probably meant abs( box2dObj.getRotation() ) < STEP instead of abs( box2dObj.getRotation() < STEP ) because it doesn't make sense to take absolute value of a bool. In it's current form, the condition would always be true for all negative rotations.

Secondly, you compare a floating point value with equality: abs( fmodf(box2dObj.getRotation() , 2*pi) ) == 0 ). That isn't going to work well because floating point values are not exact and their operations have numerical errors. ++box2dObj.getRotation() might never (likely not within even a lot of iterations) reach a value that is exactly or even near 0 (mod 2*pi).

Thirdly, you have no condition for the case fmod(r, pi) == 0 && fmod(r, 2*pi) != 0 && r > STEP, where r is the rotation. In this case the object will not be rotated further nor set upright. This isn't very likely case unless STEP is a fraction of pi though.

Also, 1 radians is quite a large value for STEP in my opinion, but suit yourself.

What you probably want is (I haven't tested this code):

if( fmod(abs(box2dObj.getRotation()), 2*pi) < STEP )
    box2dObj.setRotation( 0 )
else if( box2dObj.getRotation() > 0 )
    box2dObj.setRotation( box2dObj.getRotation() + STEP );
else
    box2dObj.setRotation( box2dObj.getRotation() - STEP );

This code should test if an object has a rotation that is at most a STEP away from a full rotation and in that case sets it to zero. Otherwise the code assumes that objects with positive rotation are rotating in positive direction and vice versa.

what can I do to find when the rotation is at a multiple of 2*pi ?

You were pretty close. As you can see from my code, I prefer to take the absolute value before fmod. This is because there are 3 different ways the modulo operation can behave with negative values (truncated, floored and euclidean) and I find it hard to remember which method is used in which standard / platform.

Then there is the equality problem. You must never test floating points values with equality (except in exceptional cases). Instead, if you want to know if a value - in this case fmod(abs(box2dObj.getRotation(), 2*pi) - is close to zero, you must test whether the value is within some margin of error. Since the value always changes by no less than STEP, it is an appropriate choice for error margin. This assures that object cannot rotate over the upright position without being caught by the test. In some other case, you might want to use much smaller margin to test equality, but it's rarely correct to test against exact value without some error margin.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top