Stepper rotation confusion

You are about to reply to a thread that has been inactive for 1345 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Hi everyone have a great new year with much love in it.xD

I am scratching my blonde head with rotations in LSL and real angles  and think I am missing something I have written this routine to work like a stepper motor in RL so it calculates zero from its current position then adds on the rest from zero to give you the rotation angle from existing angle ( confussed lol since last year when I started this and have got nofurther )

// Rememeber check corect axis this Floor has y for horizontal origin.
// takes a degree angle and turn it into a stepper angle from 0 degrees

float _getRotInAngleReqToBringToAngle(float angle) {

if(euler.y>361) return_value = _fmod(360,euler.y); else return_value = euler.y;

if(llFabs(return_value) != return_value && return_value !=180) return_value = 360+return_value; else if((integer)return_value == 0 && angle == 0) return_value = 180; else return_value = llFabs(return_value) ;

llOwnerSay("Adjusted for negative cater for -0 and -180:"+(string)return_value);
return_value = return_value + angle ;
llOwnerSay ("Original Angle: "+(string)euler.y+"Angle: "+(string)return_value+ "Angle To set to : "+(string)angle);

return return_value;
}

Now this works upto a point but goes strange at 0 degrees or past 180 . I am feeding angles  like this

if(llList2String(r_list,1)=="All Up") {MoveRootRot(_getRotInAngleReqToBringToAngle(0), "z", LINK_THIS);}
else if(llList2String(r_list,1)=="All Down") {MoveRootRot(_getRotInAngleReqToBringToAngle(180), "z", LINK_THIS);}
else if(llList2String(r_list,1)=="All Left") {MoveRootRot(_getRotInAngleReqToBringToAngle(90), "z", LINK_THIS);}
else if(llList2String(r_list,1)=="All Right") {MoveRootRot(_getRotInAngleReqToBringToAngle(270), "z", LINK_THIS);}

But they don't always work depending on the current rotation. Can anyone help before i have no hair left

Thanks for looking I know  the code line above is a simple use of it but eventually I want to be able to provide it with any angle . Can anyone help no sarcastic comments please .

Edited by VirtualKitten
Share on other sites

3 hours ago, VirtualKitten said:

if(euler.y>361) return_value = _fmod(360,euler.y); else return_value = euler.y;

if(llFabs(return_value) != return_value && return_value !=180) return_value = 360+return_value; else if((integer)return_value == 0 && angle == 0) return_value = 180; else return_value = llFabs(return_value) ;

This is why everybody in graphics uses a homogeneous representation of rotations. No special cases at zero degrees. In Second Life, that's quaternions.

The key idea here is that you can multiply LSL rotations to get a new rotation. So, compute the rotation quaternion for the change you want to make using llEulerToQuat, then multiply the object's old rotation (from llGetRotation or llGetLinkPrimitiveParams) by the quaternion for the change to get the object's new rotation. Then apply that to the object with llSetRotation or llSetLinkPrimitiveParams.

This area is confusing but well documented.

If you just want to spin something, see the functions which mention "Omega".

Share on other sites

Thank for looking Nova

I can't spin it with Target Omega its a linked-set as it has two more moving components . Yes Nova am using llSetLinkPrinitiveParamsFast( in my MoveRootRot function and it was not worthy of mention as is not the problem . The problem is that when i have a position in a circle that the stepper is facing when it is set at 0 as in description it has nothing to go on to get back to zero. Likewise past 180 seems a problem there well may be others but haven't found exactly what they are which is messing up this stepper positioning it should always calculate an offset from zero.

Share on other sites

Animats I think you are missing what this code is trying to do sorry but dont think any of that would help meas I sad the moment is accomplished its just the linden degrees from an object to return a pointer to 0 and calculate  offset as a stepper motor does as it con-senqually travels in a circle

Share on other sites

20 hours ago, VirtualKitten said:

if(euler.y>361) return_value = _fmod(360,euler.y); else return_value = euler.y;

So what happens when you have a value between 360.0001 and 360.99999 ?  (If you are going to feed integer values to things that use floats be prepared to suffer the default rounding, which might not be at all what you believe it will be)

Animat's advice to use quaternions is the best one, but if you must use Euler there's no reason why you can't, with a bit of extra coding.

Edited by Profaitchikenz Haiku
Share on other sites

Because my script doesn't work like that and don't understand him  Its just confusing me

Share on other sites

I understand, sometimes I don't even understand myself.

Seriously though, in that piece of code, check against 360.0 (not  360) and use >= so that if the Y value is >= 360.000 you subtract 360.000 from it and revert to 0.0.

The reason for specifying .000 is an old one and possibly modern compilers do things better now, but it is to remind yourself (and other coders if you're on a team) what degree of floating point precision you wish. If you say a number is 360.0 then it could be anywhere in the range 359.95999 to 360.049999, both are the extents of a range that rounds to 360.0. If you say 360.00 then you are specifying a tighter range, 359.995999 to 360.004999, and so on.

Edited by Profaitchikenz Haiku
• 1
Share on other sites

@VK, after a quick play in a sandbox, I think I see your problem, it is where you are looking for negative angles:

you test for a negative value that is NOT -180 and act accordingly, but then the other if---else clauses handle everything EXCEPT -180, so you need to add a clause to explicitly handle -180. (Which if I understand what you are trying to do requires you to invert it)

I made the alterations that I suggested above and dropped the integer comparisons as well. eg return_value = 0.0 && value = 0.0, but this is rather sloppy coding, you should ideally test for llFAbs(return_value) <= 0.001 or some similar tolerance figure

Edited by Profaitchikenz Haiku
• 1
Share on other sites

6 hours ago, Profaitchikenz Haiku said:

So what happens when you have a value between 360.0001 and 360.99999 ?  (If you are going to feed integer values to things that use floats be prepared to suffer the default rounding, which might not be at all what you believe it will be)

Comparing ints with floats is "accurate," there won't be any rounding involved.

```llOwnerSay((string)[ (0.1 * 10) > 1 ]); // output: TRUE

// 0.1 is not a representable float.
// The actual value is 0.100000001490116119384765625

// 0.1 * 10 = 1.00000001490116119384765625
// ...which is greater than 1```

5 hours ago, Profaitchikenz Haiku said:

The reason for specifying .000 is an old one and possibly modern compilers do things better now, but it is to remind yourself (and other coders if you're on a team) what degree of floating point precision you wish. If you say a number is 360.0 then it could be anywhere in the range 359.95999 to 360.049999, both are the extents of a range that rounds to 360.0. If you say 360.00 then you are specifying a tighter range, 359.995999 to 360.004999, and so on.

Do you have a source (or demonstration) for this? Because that doesn't seem to be true at all, and would be extremely unreliable behavior and not compliant with the IEEE standard LSL uses. 360 and 360.0 and 360.000 all get converted to the exact same float value; precisely 360.0. There is no "implicit rounding" of floats within LSL beyond converting them to strings.

Edited by Wulfie Reanimator
Share on other sites

36 minutes ago, Wulfie Reanimator said:

Do you have a source (or demonstration) for this?

Nope. I'm going by an old coding standard I worked to on one of my earliest projects, which was that when specifying floats as literals you should indicate how many places of precision you wanted to see to the right of the decimal point. It's possible they were over-prescriptive, it's too late now to go and ask them.

Do bear in mind I began coding before some of these standards became commonplace

But it does make sense to me. If somebody says 9.5 I understand them to mean "I'm Ok with what you give me so long as it round to 9.5", so if it's a piece of wood they've asked for I'll cut it so that it's 9.45 to 9.55. But if they say 9.50 I'll cut it so it's 9.405 to 9.505.

And now I suspect you'll want to query the value of the kerf

Share on other sites

9 minutes ago, Profaitchikenz Haiku said:

Do bear in mind I began coding before some of these standards became commonplace

I wasn't aware. However, the current IEEE-754 standard has been in place for 35 years.

Share on other sites

can simplify this by using llAxisAngle2Rot  http://wiki.secondlife.com/wiki/LlAxisAngle2Rot

a usage example to rotate a object on its local Y axis

```rotation start_rot;
float angle;

default
{
state_entry()
{
start_rot = llGetLocalRot();
}

touch_start(integer total_number)
{
// angle in degrees
angle += 13.0;
// angle -= 10.0; // -angle will rotate in the opposite direction

// fmod longhand
if (angle > 360.0)
angle -= 360.0;
else if (angle < -360.0)
angle += 360.0;

// rotate on the local Y axis <0, 1, 0>
llSetLocalRot(llAxisAngle2Rot(<0, 1, 0>, angle * DEG_TO_RAD) * start_rot );
}
}```

• 1
Share on other sites

@Mollymews Thanks Molly for the example but its in a linked set and stepping is not a problem imagine you have ten stepping around and they do it at different rates the code above is to reset them all from their current position to work out the offset and get them at this position . I think your example is just stepping around, thanks for looking.

Share on other sites

the axis order to calculate a rotation is Z, Y, X and it uses shortest path

the effect of this is is that <180,0,0> results in <180,0,0> whereas <0,180,0> results in <180,0,180> so your function has to take this into consideration

a alternative is to build the device to operate on the X axis, when so then the issue you have goes away

for myself then if I wanted to continue with the Y axis then I would refactor the app to use llAxisAngle2Rot and its companions llRot2Angle, llRot2Axis and llAngleBetween

Share on other sites

Molly it depends on the blender model and the import as to the orientation please look at my code if you have time in first pot and let me know what you think is wrong thanks for looking

Share on other sites

28 minutes ago, VirtualKitten said:

if you have time in first pot and let me know what you think is wrong thanks for looking

"the axis order to calculate a rotation is Z, Y, X and it uses shortest path

the effect of this is is that <180,0,0> results in <180,0,0> whereas <0,180,0> results in <180,0,180> so your function has to take this into consideration"

when we read the local rotation and convert to euler we do not get <0, 180, 0> we get <180, 0, 180>. euler.y = 0 and not 180 as we might presume.  Therefore 0 + 90 = 90 and not 180 + 90 = 270 as presumed. We can see this:

```default
{
state_entry()
{
vector euler = <0,180,0>;

vector result = llRot2Euler(llGetLocalRot()) * RAD_TO_DEG;

llOwnerSay((string)result);
}
}```

so this is what is wrong with your current code. The code doesn't compensate for the resulting behaviour of the method used internally by LSL to calculate rotations

Share on other sites

Molly thanks for your kind help , but you have confused me sorry . My axis is not changing in original post its static to between 0 - 360 . I know my post has errors or it would do what i expected i was hopeful someone knew why my apologies if i have not understood am not having a good day sorry Are you saying vector euler = llRot2Euler(llList2Rot( llGetLinkPrimitiveParams(LINK_THIS,[PRIM_ROT_LOCAL]),0))*RAD_TO_DEG; is wrong and should be something else?

if(llList2String(r_list,1)=="All Up") {MoveRootRot(_getRotInAngleReqToBringToAngle(0), "z", LINK_THIS);}
else if(llList2String(r_list,1)=="All Down") {MoveRootRot(_getRotInAngleReqToBringToAngle(180), "z", LINK_THIS);}
else if(llList2String(r_list,1)=="All Left") {MoveRootRot(_getRotInAngleReqToBringToAngle(90), "z", LINK_THIS);}
else if(llList2String(r_list,1)=="All Right") {MoveRootRot(_getRotInAngleReqToBringToAngle(270), "z", LINK_THIS);}

// Rememeber check corect axis this Floor has y for horizontal origin.
// takes a degree angle and turn it into a stepper angle from 0 degrees

float _getRotInAngleReqToBringToAngle(float angle) {

if(euler.y>361) return_value = _fmod(360,euler.y); else return_value = euler.y;

if(llFabs(return_value) != return_value && return_value !=180) return_value = 360+return_value; else if((integer)return_value == 0 && angle == 0) return_value = 180; else return_value = llFabs(return_value) ;

llOwnerSay("Adjusted for negative cater for -0 and -180:"+(string)return_value);
return_value = return_value + angle ;
llOwnerSay ("Original Angle: "+(string)euler.y+"Angle: "+(string)return_value+ "Angle To set to : "+(string)angle);

return return_value;
}

Edited by VirtualKitten
Share on other sites

Molly's answer makes sense to me (and explains the odd figures I've seen in the build floater for years but just accepted), but when you say "my axis is not changing, it's static within 0 - 360) all I can assume is that you want the Y-axis of the child prim to alter it's value according to how the root prim rotation has changed, i.e to perform as a gyroscope might do and attempt to resist turning forces.

Is there any way you could make a test harness with only two prims involved to demonstrate this problem such that others can try to replicate it? I still don't really understand what it is you're trying to do, let alone how this problem is actually affecting things.

Edited by Profaitchikenz Haiku
geriatria
Share on other sites

13 hours ago, VirtualKitten said:

Are you saying vector euler = llRot2Euler(llList2Rot( llGetLinkPrimitiveParams(LINK_THIS,[PRIM_ROT_LOCAL]),0))*RAD_TO_DEG; is wrong and should be something else?

the value of euler is not wrong per se. It is just not what your code is anticipating it to be. Your code as wrote anticipates euler.y to be in the range [0 < 360] and the X and Z axis values to remain unchanged when Y changes

to better help understand what happens when rotations (quaternions) are converted to euler then is best to enter values into the Build Editor directly and see the results

what we see in the Build Editor is the same result of llRot2Euler() in our scripts

for example we type <0, 180, 0> into the Build Editor. This euler (vector) value (in degrees) is converted to a quaternion. This quaternion is applied as a rotation to the object. Then the newly applied quaternion is read and converted back to euler, this euler value (in degrees) is displayed in the Build Editor

<0,179,0> convert to quaternion. Convert quaternion back to euler vector results in -> <0,179,0>
<0,180,0> convert to quaternion. Convert quaternion back to euler vector results in -> <180,0,180>
<0,181,0> convert to quaternion. Convert quaternion back to euler vector results in -> <180,359,180>

<0,269,0> -> <180,271,180>
<0,270,0> -> <0,270,0>
<0,271,0> -> <0,271,0>

<0,89,0> -> <0,89,0>
<0,90,0> -> <0,90,0>
<0,91,0> -> <180,89,180>

<0,359,0> -> <0,359,0>
<0,0,0> -> <0,0,0>
<0,1,0> -> <0,1,0>

so when we are working with the Y axis then our code has to be written with a clear understanding of how quaternion to euler conversion is done by the SL engine

is possible to write a translation table/function to map the result of PRIM_LOCAL_ROT to the range <0,0,0> ... <0,359,0> but my recommendation is not to work on the Y axis and avoid all the hassle that you are currently experiencing in attempting to do this

suggest that you go back into Blender, reorient the objects to operate on the X axis. Because X is always in the euler result range <0,0,0> .. <359,0,0>

<0,n,n> -> <0,n,n>
<91,n,n> -> <91,n,n>
<181,n,n> -> <181,n,n>
<269,n,n> -> <269,n,n>
<359,n,n> -> <359,n,n>

if tho you do continue wanting to work on the Y axis then you will need to write your function: _getRotInAngleReqToBringToAngle(float angle) as a translation table function, mapping the returned euler value of llRot2Euler(.. PRIM_ROT_LOCAL .. ) to the range <n,0,n> .. <n,359, n>

Edited by Mollymews
ROT_LOCAL
• 1
• 1
Share on other sites

Hi Molly

thank you for that great reply I had not understood you was talking about quaternions . I should state the Y axis is an anomaly of my mesh orientation and not actually Y in world per ce. However your wonderful explanations of quaternions is making me understand a little better that they contain more information that might help me. Perhaps looking at the Y not really the Y.  I need to look at other  axis too in my calculations to derive a position at 0 and at 180. This is  becoming more complicated sighs.

Thanks for your assistance Denise X

• 1
Share on other sites

You are about to reply to a thread that has been inactive for 1345 days.

Please take a moment to consider if this thread is worth bumping.