1

I have a quaternion representing the orientantion of an object (yellow box and sphere). I would like to know if it is possible to split that quaternion into other quaternions that give us the rotation of each local axis (X, Y and Z).

Due to I do not have enough reputation to post images directly, I have to put just the link of the image

What I have been doing until now is getting the Euler representation and work with it, but it is not the correct solution for my particular case:

Given two points (blue boxes), I want to limit the orientation of my object so that it can't point out of the grey plane, even if my quaternion looks out of that plane.
I want to split (decompose) the quaternion, because when my object reachs the limit of the plane (for instance, the right), I want to make it stay there (in that component), and then rotate my object in the vertical component, using one of the new splitted quaternion.

I am working with Unity.

I hope it is understandable my problem :)

Pᴇʜ
  • 56,719
  • 10
  • 49
  • 73
javinair
  • 55
  • 1
  • 5
  • would `Quaternion rotation = Quaternion.Euler(new Vector3(transform.eulerAngles.x, 0, 0));` be applicable? – Ryanas Apr 25 '17 at 09:25
  • Thanks @Peh for the edition. Thanks Ryan, but I want to avoid Euler angles because the original quaternion comes from an external IMU, and limiting the orientantion with Euler angles I have had some troubles – javinair Apr 25 '17 at 09:58
  • [Quaternion.eulerAngles](https://docs.unity3d.com/ScriptReference/Quaternion-eulerAngles.html) is a Thing, but I suspect that's what you're already using. I know the `wxyz` values won't help at all as individually they're incomplete. Best I can think of is to calculate a Euler `AngleTo()` (you'll have to write this method) on the two blue boxes and check that both the X and Y components thereof are greater than 0 (or lessthan, as appropriate). – Draco18s no longer trusts SE Apr 25 '17 at 13:39
  • http://stackoverflow.com/questions/3684269/component-of-a-quaternion-rotation-around-an-axis?rq=1 1.Decompose 2 Limit 3. Compose – minorlogic Apr 26 '17 at 09:08

2 Answers2

5

Here's a way to get the local rotation of just the y-axis. This function can be modified to get the x or z-axis.

/// <summary> isolate the y-Component of a rotation </summary>
private Quaternion yRotation(Quaternion q)
{
    float theta = Mathf.Atan2(q.y, q.w);

    // quaternion representing rotation about the y axis
    return new Quaternion(0, Mathf.Sin(theta), 0, Mathf.Cos(theta));
}

You can verify the result in the Unity inspector by converting to Euler:

public float yLocal;
void Update()
{
    yLocal = yRotation(this.transform.rotation).eulerAngles.y;
}
ShawnFeatherly
  • 2,470
  • 27
  • 20
0

I have worked with IMUs before and to my knowledge with Unity if the object is reading in the IMU data as quaternions, then you will not encounter the gimbo lock problem. So you can indeed create a reference to the object's rotation and convert those to euler angels, and then convert back to Quaternions before applying it to the object.

However, if you are simply wanting to limit rotation I would do something like this:

//the last rotation was pointed at the grey zone.
private Quaternion prevAllowedRotation;

void FixedUpdate(){
    if(!isValidRotation()){
        this.transform.rotation = prevAllowedRotation;
    }else{
        this.transform.lookAt(lockToArea());
    }
}


private bool isValidRotation(){
    //I chose forward based on the image, find the direction that works for you
    Ray ray = new Ray(this.transform.position, this.transform.forward);
    RaycastHit hit;
    if(Physics.Raycast(ray, out hit, 10f){
        if(hit.transform.tag == "wall"){
            return true;
        }
    }
    return false;
}


private Vector3 lockToArea(Gameobject grey){
    Vector3 center = grey.transform.position;
    //figure out how to get the position of the facing direction on the same plane as the grey area.
    Vector3 pointerPosition = getPointerPosition();
    RaycastHit hit;
    if(Physics.Linecast(pointerPosition, center, hit)){
        return hit.point;
    }
    return Vector3.zero;

}

private Vector3 getPointerOnPlane(){
    Ray ray = new Ray(this.transform.position, this.transform.forward);
    // you need to figure out how to dyanmically get the distance so that the ray lands on the same plane as the vector
    float distance = 0; 
    return ray.GetPoint(distance);       
}

This locks its rotation to only be pointing at the grey cube. This should help you a lot, you just need to get the point that the IMU data is reading in at and is on the same plane as the grey cube's center.

Dan Flanagan
  • 504
  • 2
  • 13
  • thanks @dan for your answer. It is really close to what I expect, but I want to go one step beyond. My rotation is limited to the area of the grey plane, OK, but the problem now is when my cube is pointing out of the plane (the raw rotation I mean), it is stuck at one side of the plane. For instance, if I get my cube out of the right side of the plane and move it vertically, it remains stuck in the last allowed position, instead of move up and down along the right edge. Is it easy to reach? Thanks again. – javinair Apr 27 '17 at 07:39
  • @javinair look at the latest edits I made, I didn't have enough time to do the last step which was finish the getPointerOnPlane but if you figure that out, then you have exactly what you want. I made these edits after I re-read your question. I am also glad it worked, as I wasn't able to test it XD. – Dan Flanagan Apr 27 '17 at 21:57