# Rotate Vertically Around Any Point and Any Y Axis? (ferris wheel)

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

## Recommended Posts

Thanks to the help I received last time I was able to learn about rotating an object around an offset  axis in 2D but now Im wondering how to rotate around a point with a 3rd axis. This time I'm working on a ferris wheel. I have the seats spinning around an offset axis on Z rotation but if you change the Y angle the seats do not follow. I've tried modifying the same equation I used before but I know there is something missing.

I imagine what I need to do is something like below, figure out the x and y on the bottom triangle and then apply the z axis. I can get the Z pretty easily but I am not sure how to get the X and Y.

﻿

This is what I am currently using the apply the position of the seats when they are spinning on the Z axis.

```seatPos(vector baseRot, vector basePos)
{
//llOwnerSay((string)basePos);
{
}
string name = llGetObjectName();
string substring = llGetSubString(name, -1, -1); //Get Seat Number
float angleAdjust = ((360/(integer) seatCount) * (integer)substring);
count = -baseRot.z + 90 + angleAdjust; //Modify position by seat number.

float angle = (PI/180)*count;
float seatX = basePos.x + radius*llSin(angle); //calucate X position
//float seatY = basePos.y + radius*llCos(angle);
float seatZ = basePos.z + radius*llCos(angle); //calculate z position
//llOwnerSay((string)radius + " " + (string)angle);
//llOwnerSay ((string)seatX + " " + (string)seatZ);

list params = [PRIM_POSITION, <seatX, basePos.y, seatZ>];

}```

If someone knows of any resources to where I could learn about the math involved in what I am trying to do I would highly appreciate it. I've tried to look into sin, cos, tan and that helped with understanding the equations of the flat position but I'm not grasping the addition of a 3rd point.

##### Share on other sites

I was initially confused becuase I call a Ferris wheel the giant wheel in the sky rotating around a horizontal axis, but from what you are saying, it sounds like you are building a wheel turning parallel to the ground with seatrs that dangle from it and fly outwards as the wheel speeds up?

There is more than one angle varying here because of the extra vector component. The position of your X and X is initially a straightforward circular motion, but then variation in the vertical angle is going to cause some change in where X and Y are as well as Z, since the hieght of the seat will vary, In effect, as the seat swings outwards by centrifugal force the radius of the circle is increasing, and the distance of the seat below the pivot point decreasing.

The order of the calculation must therefore be

1, establish the angle that the seat has moved away from the vertical due to centrifugal force, and from this establish the increase in Z position to be applied, and the increase in radius.

2 add the radius increase to the main radius and calculate the new X and Y according to the (different) angle that the wheel as now tirned to

3 calculate the Z figure

Apply the resulting vector.

##### Share on other sites

Whoops, was my description that bad? I meant a regular ferris wheel as you know it, rotating around a horizontal axis. Although you seem to have given a possible solution for what is called an enterprise. This is an enterprise if you don't know:

That would be fun to make sometime but I would need to learn how to get the "extra vector component", as you call it, for the rotating arm.

For now Im trying the ferris wheel which works but only if it is placed at 90 degs, 180 degs, etc. I am trying to get the seats to spin around the same axis even if the ferris wheel was placed at a 33 degree angle for example. I've gotten close but almsot something off with what I have tried.

##### Share on other sites

OK, I hadn't realised what you were aiming for. Maybe a simple way of looking at it is this:

Around a given central point, you want to plot XYZ tripples which will be describing a great circle on a sphere of radius R, and you could obviously look up some spherical geometry articles. Or, you can simplify it into a varying radius problem:

Z is going to vary in a predictable manner, since it is varying up and down around a central point, so it is a simple calculation using Sin of the angle times the radius to give a value that is added to or subtracted from the central Z value. viewed from the same angle as the vertical plane, Z is simply moving up and down in a straight line.

X and Y are going to be moving along a plane which, when viewed from above, appears to be a line going from X1, Y1, to X2, Y2, again using sin of the Angle. It is slightly more complex in that when looking down on the path of any single seat from above, it is not moving in a circle, it is moving apparently between the two extreme points in a straight line,

So, the radius is varying with changing angle for X and Y. You can probably take the sin of the angle, and multiply the radius by it, giving you a value varying between -radius, 0, and + radius, and use this to establish where X and Y are to be plotted, using trig to determine X and Y from new R.

Solve each equation seperately for varying angle, then use the resultant X, Y and Z, and you have your locus point.

There is a point where the increasingly complex calculations make it worthwhile changing from using basic trig functions to using quaternions and rotations, and you might be in the vicinty of this point I would be intersted to hear from other scripters more at home with Rotations to know if this problem is simpler with quaternions.

##### Share on other sites

LSL provides some powerful functions that deliver rotations (quaternions) and all it takes are some vectors or axis or angles.

For the present task you may use: llAxisAngle2Rot( vector axis, float angle );
The arguments almost speak for them self
axis is any axis in space
angle is the angle to rotate about the axis

To create a spinning object increment the angle in small rapid steps

:smileysurprised::smileyvery-happy:

##### Share on other sites

You may also create the spin in two dimensions and then 'add' a major or basic rotation in the end

Her is a line that computes positions for carriages on a ferries wheel
It is computed for positions in the X,Z plane and then rotated to any basic rotation you want by 'multiplying' with baseRot

`V = radius*< llCos( angl), .0, llSin( angl)>*baseRot;`

:smileysurprised::smileyvery-happy:

##### Share on other sites

llAxisAngle2Rot(); is interesting but I'm not sure how you might be implying I could use the function in my script. I'm not currently use any rotations except on the angle of the ferris wheel body. The seats are seperate objects and so I have been using position, not rotation to move them in space.

Sorry, my title does imply I am trying to rotate around an axis. I guess I should have specified that I am trying to move an object in a circular motion around an axis as if it were rotating.

Although your second suggestion seems very useful. I'll give it a try, thanks! Although, to be clear, is your equation also returning a rotation or is it a vector?

Edit: I gave it a try but without luck. I used the equation to calculate the X and Z vector and it didn't succeed in going around the axis. I replaced my equation with your equation, was that what you had in mind?

##### Share on other sites

It will be a vector, because the first part where a float (radius) is used to multiply a vector yields a vector, and then multiplying by a rotation further transforms the vector.

I do like the simplicity of the solution, but I am still struggling to comfortably visualise it in the same way that I can see trig functions inside my head. I think my platonic world must be a very old version.

##### Share on other sites

Thanks, I thought so but wasn't sure.  I will try applying it again with that information.

Edit: Tried applying it as such with no luck:

`seatPos(rotation baseRot, vector basePos){    if (radius == 0)    {        radius = llVecDist(myPos, basePos);    }    string name = llGetObjectName();    string substring = llGetSubString(name, -1, -1); //Get Seat Number    float angleAdjust = ((360/(integer) seatCount) * (integer)substring);    count = -baseRot.z + 90 + angleAdjust;        float angle = (PI/180)*count;    vector pos = radius*< llCos( angle), .0, llSin( angle)>*baseRot;        list params = [PRIM_POSITION, pos];    llSetLinkPrimitiveParamsFast(LINK_THIS, params);}`

Am I doing something very wrong in the way I've applied it? Otherwise I guess I do not understand what was meant by the equation. I did realize that I wasn't use a rotation for baseRot instead a vector but I changed it to an actual rotation. Also isn't the base position needed somewhere in the equation? Even though I could be totally wrong I did try: (radius*< llCos( angle), .0, llSin( angle)>+basePos)*baseRot; which still led me to nowhere.

##### Share on other sites

Yes, my guess from reading it is that it will produce a vector with both X and Z varying between + and - radius, so you would want to add it to whatever your wheel centre position is.

I would start off with a simpler investigation. Take a cube and using Dora's equation, try to rotate it in an arc around a centre point with a fixed radius of 3.0 . If neccessary, stick some llOwnerSay lines in there to chat out the angle count and resultant vector.

Initially set baserot to ZERO_ROTATION, and once you have the cube going round and round, try altering baseRot ( for example, baseRot = llEuler2Rot(<0.0, 0.0, 30.0>*DEG_TO_RAD) )

##### Share on other sites

To move the seats round a centre point, how about something based on this (which I based on the example in the wiki, Position of Object Rotated Around A Relative Point)?:

`vector vPosOffset =<0.0,0.0,3.0>;vector vStartPos;rotation rStartRot;rotation vRotArc;integer counter;integer max =36;default{    state_entry()    {        vRotArc = llEuler2Rot(<0.0,-10.0,0.0>*DEG_TO_RAD);    }    touch_start(integer total_number)    {        vStartPos =llGetPos();        rStartRot =llGetRot();        llSetTimerEvent(0.25);    }        timer(){        if(++counter<36){        llSetPrimitiveParams( [PRIM_POSITION, llGetPos() + (vPosOffset - vPosOffset * vRotArc) * llGetRot()]);       vPosOffset = vPosOffset * vRotArc;           }        else{           llSetTimerEvent(0.0);           counter = 0;           llSetPos(vStartPos);           llSetRot(rStartRot);         }            }}`

I'm not quite sure how (other than by calculating a look-up table) how to calculate the start positions for the other cars, but I'll try to puzzle it out, unless some kind person who understands trignometry can explain it.

##### Share on other sites

Supposedly, this page should explain the lot, but each time I go there, I come away from it feeling that I know less than when I went there. I don't know if I have now lost too many brain cells to grasp the maths, or if I'm just being bamboozled by a brilliant piece of obfustication.

Dora's suggestion worked well enough for me when I had a play around. I should have realised that multiplying a vector by a rotation was the most direct way of swinging something around.

For your suggestion, I think using the trick of just chatting the count to the other seats would also work.

What I think I would like to work out is which of the methods proposed is most efficient in script time. Quite a few maths co-processors implemented Sin and Cos on-chip, which made me assume that using them might be quicker.

• 1
##### Share on other sites

I was using that example from the wiki but, if your is the same, it's not possible in the long run. It runs based on the seats position, if I change the position the offset axis is also changed, so I cannot change the seat positions as I have in some rides. I've been keeping the rotation seperate and it works just not when it comes to rotating the ride on a vertical axis with the seats rotating on a horizontal axis which is what I am trying to do..So it forces me to keep the seat at the same angle. But thanks for the suggestion.

##### Share on other sites

Thanks for pointing out  that is gives me the difference between X and Z and to Dora for the equation. I got it working and it works quiet amazing. Although I don't understand why does adding the baseRot onto the X,Z spin cause it to rotate around?

Also would it be too much to ask you guys one more question involving rotations? Now instead of just up and down rotation, and side to side rotatation, what about diagonal rotation?  So the seats will be effected by the  at a diagonal angle, instead of X and Z spin it would X and Y spin with Z offset or it could be an X and Z spin, like now, with a Y offset. I've test with the equations and can get a diagonal spin by changing the Y to llSin(angle) or llCos(angle) but that always spins diagonally.

Thanks again guys!

##### Share on other sites

1. baseRot does not create the spin, it just orientates the wheel in space
2. Give it a base rotation of baseRot = llEuler2Rot( DEG_TO_RAD*< 45, 0, 0>); and the wheel will tilt 45°

:smileysurprised::smileyvery-happy:

• 1
##### Share on other sites

Oh, it really was that simple. I had thought I had already tried that, or the equivalant of applying X/Y angle from the base, before asking but tried again since you suggest it and found out my base was broken.

Thanks so much. Due to all of the genorosity in helping accomplish the rotation for my scripts I've cleaned up the scripts and am sharing them below; sharing is the least I can do for now. Here is the parent (static axis) and child (object to rotate) script. Everything is controlled by the parent script. You would just need to change the listen channel in the child script.

Parent (Axis):

`float radius = 5.0;float timerSpeed = .05;float rotStep = .25; float count;vector myPos;integer listenChan = -360;integer touched = FALSE;rotation turnRot;rotation myRot;seatPos(){    myPos = llGetPos();    myRot = llGetRot();    count += rotStep; //Set axis rotation angle.    if (count > 360)    {        count -= 360;    }        vector euler = RAD_TO_DEG * llRot2Euler(myRot);     vector rot = <euler.x,euler.y,count>; //Apply axis rotation angle.    //llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_ROTATION, llEuler2Rot(rot*DEG_TO_RAD)]); //Can set axis prim to rotate with child object.    list temp = ["position", myPos, "rotation", rot, "radius", radius];    llShout(listenChan, llList2CSV(temp));}default{    state_entry()    {        llListen(0, "", llGetOwner(), "setup");    }    touch_start(integer total_number)    {        if (touched == FALSE)        {            llSetTimerEvent(timerSpeed);            touched = TRUE;        }        else if (touched == TRUE)        {            llSetTimerEvent(0);            touched = FALSE;        }    }    timer()    {        seatPos();    }}`

Child (Rotated Prim):

Although this is still a bit bumpy. Hard to set the proper rotation angles based on the base prim. The SL build system rotations doesn't translate well for that application. You also have to change some things if you want it to rotate around a horizontal axis.

##### Share on other sites

Short and pretty:) Congratulations.

I'm not sure what you refer to when you say: 'The SL build system rotations doesn't translate well'
My guess is that your child relies on a chat message every 0.05 seconds to move smoothly
and you can not guarantee that because chat is not served instantaneously and without delay.
To overcome that you could minimize the chat by only sending initial data and maybe start and stop commands.
The child script should be self contained and be able to loop the angle in small, fast increments.

Secondly I don't see why you can't make it spin with a horizontal axle
The basic cirle has a horizontal axis, the Y-axis

```float angl; // incremented by small amounts, 0 <= angl < TWO_PI
vector V = < llCos( angl), .0, llSin( angl)>;```

With baseRot = ZERO_ROTATION it should.

Finally a small detail: llRegionSay() is prettier and course less lag than llShout()

:smileysurprised::smileyvery-happy:

• 1
##### Share on other sites

I'm not saying this is any better than your method but, simply for reference, this seems to work for me, whatever the rezzer's rotation, with the cars calculating their next move each time the rezzer (which they move round) tells them to.   Note the new calculation:

`vPosOffset = (vRezzerPos-llGetPos())/llGetRot();`

In the rezzer/centre of rotation

`integer toggle;integer giCommsChan = 15045;integer giRandChan;integer counter;integer handle;integer max=5;string object;rotation vRotArc;rotation rRot;vector vPos;vector vPosOffset = <0.0,0.0,2.0>;default{    state_entry()    {        object = llGetInventoryName(INVENTORY_OBJECT,0);        vRotArc = llEuler2Rot(<0.0, (360.0/(float)max),0.0>* DEG_TO_RAD);            }        changed(integer change){        if(change & CHANGED_INVENTORY){            llResetScript();           }    }    touch_start(integer total_number)    {        if(toggle=!toggle){        llOwnerSay("turning on");        llRegionSay(giCommsChan,"die!");        counter =0;        vPosOffset = <0.0,0.0,2.0>;        vPos = llGetPos();        rRot = llGetRot();        vPosOffset*=vRotArc;        llListenRemove(handle);        giRandChan = (integer)llFrand(1000000.0)+1000;        handle = llListen(giRandChan,"","rezzed","");                llRezAtRoot(object,        vPos+vPosOffset*rRot,        ZERO_VECTOR,        rRot,        giRandChan);        vPosOffset*=vRotArc;         }                else{        llOwnerSay("turning off");          llSetTimerEvent(0.0);         llRegionSay(giCommsChan , "stop!");        }    }    listen(integer channel, string name, key id, string message)    {               llRegionSayTo(id, channel, "hi!");        llListenRemove(handle);                 if(++counter<max){        giRandChan = (integer)llFrand(1000000.0)+1000;        handle = llListen(giRandChan,"","rezzed","");                llRezAtRoot(object,        vPos+vPosOffset*rRot,        ZERO_VECTOR,        rRot,        giRandChan);        vPosOffset*=vRotArc;         }        else{            counter =0;            llSetTimerEvent(1.0);            llRegionSay(giCommsChan, "tick!");        }    }    timer()    {                llRegionSay(giCommsChan, "tick!");    }}`

and in the car:

`key gkRezzer;vector vRezzerPos;vector vPosOffset;vector vStartPos;rotation rStartRot;rotation vRotArc;rotation rRezzerRot;integer giCommsChan = 15045;integer tempChan;integer handle;default{    state_entry()    {        vRotArc = llEuler2Rot(<0.0,-10.0,0.0>*DEG_TO_RAD);    }        on_rez(integer p){        if(p){            tempChan = p;            vStartPos =llGetPos();            rStartRot =llGetRot();            handle = llListen(tempChan,"","",""); //for handshake with rezzer            llRegionSay(tempChan,"rezzed");//tell the rezzer we're ready to listen        }    }    listen(integer channel, string name, key id, string message)    {              if(tempChan == channel){          llListenRemove(handle);          handle = llListen(giCommsChan,"","","");          gkRezzer = id;          list params =llGetObjectDetails(gkRezzer, [OBJECT_POS,OBJECT_ROT]);          vRezzerPos = llList2Vector(params, 0);          rRezzerRot = llList2Rot(params, 1);           //now calculate offset          vPosOffset = (vRezzerPos-llGetPos())/llGetRot();        }        else if ("tick!"==message){        vPosOffset = (vRezzerPos-llGetPos())/llGetRot();        llSetPrimitiveParams( [PRIM_POSITION, llGetPos() + (vPosOffset - vPosOffset * vRotArc) * llGetRot()]);         }        else if ("stop!" == message){        llSetPos(vStartPos);        llSetRot(rStartRot);         }        else if ("die!" == message){          llDie();        }    }}`
• 1
##### Share on other sites

Oh, what I mean by mentioning the SL build system was that if you rotate the prim by build tools the resulting angle may not be what is expected in the script. For example I rotated on one axis and it changed the other axis instead. So you have to change it manually in the build tools or it may be best to set rotation manually in the script itself.

Also thanks for the suggestion. I need messages a bit more than the bases info just at the start, incase you move the base while the script is running, but I could probably make it smoother by using less chat messages. I guess I just need the child script to save all the information which it does not right now.

You can get it to rotate, I had that wrong that you need to change the script. You just have to rotate 90 degrees on X or Y.

##### Share on other sites

I have begun to realise there is a terminology problem here. Forgetting tagetOmega as a special case of client side, LSL offers a basic type called a rotation, which can be used for various things.

"Rotation" as a term in an algorithm ca have several distinct meanings.

1) Rotate an object about a combination of the three axes to make it point in a new direction. (It's vector position is unchanged).

2) Rotate the position of an object around a point so that it moves in a path which describes a circle (it's vector position changes)

3) Rotate the object continually about one or more of it's own three axes. (special case of 1 in that it keeps going, but it's position vector still remains unchanged).

A lot of the confusion has arisen because you have been trying to do a combination of two of these, to be precise, 2) and 3), make an object rotate around it's own Z axis whilst at the same time tracing out a circular path around another point.

Then, we add in the confusion between global and local rotation, whereby if the object is a part of a link set where the central point about which it is to orbit is the root prim, it's position with respect to the root prim might include a local rotation if it is to keep pointing inwards at, outwards from, or at some defined agle to the root prim. We did at least get out of that trap by coming back to individual seats.

Your question about diagonal rotation really needs to be considered as a case of 2), rotate an object in a circular path such that it orbits a fixed point. Because there are three planes, X, Y, Z, what in effect you are doing is making the object trace out a great circle around the surface of a sphere.

The simple way to break this into steps is therefore to start out by making the object orbit in one of the three planes, and in the example given by Dora, the plane chosen was such that X and Z varied, therefore those two components in a vector were varying. In order to then rotate (turn) the vertical plane in which the object's orbit was happening, a rotation was applied as a one-shot operation to adjust the plane.

So the diagonal rotation question comes down to realising that an object orbiting on the surface of a sphere will have at least two of the three planes experiencing angular variations, and when you add in a third (diagonal), you are simply including the third plane. Iy you want to make it cant over from the vertical, add in a rotation around the Y axis.

In addition to the varying ways in which the term "rotation" and the LSL type rotation can be used, we have to add in another difficulty, the one which perhaps flummoxes me more than anything else: all this is not being done in polar coordinates but in cartesian, and this is where I think learning to use the LSL rotation type is the key, because as has been shown, a rotation can be used to transform a vector in the required way, and because of the use of cartesian coordinates, it all has to ultimately be done using vectors.

The next time somebody gives me the "It's a game" jibe about SecondLife, I think I'm going to hand them one of these rotation problems and say "tell me this is fun, and I'll agree that it's a game".

• 1
##### Share on other sites

Astronomers and physicists keep these meanings straight by using different words.  The Earth rotates around its own axis but it revolves around the Sun.  The Earth's axis is oriented roughly 23.5 degrees from the ecliptic plane of the Solar System.  Once you step away from those more specific terms and start using the word "rotation" to mean any of them, interchangeably, things get confusing.

• 1