Quaternion Rotation Applied to PVector

edited July 2014 in How To...

I'm trying to create two quaternions (one for rotation on the x-axis and one for rotation on the y-axis) and then use these to apply it to a PVector(x,y,z).


What I've done so far:

  1. I found a quaternion class on the Processing forum. I can use the fromAxis() function to generate my quaternions, but there's no way to apply it to a PVector. This forum also presents a rotate(PVector v, PVector r, float a) function. When applying this - I'm not receiving my desired results. The way I am calling this function is: rotate( myVec, new PVector( 1, 0, 0 ), myAngle). I'm using the r argument to say that this is the rotation in the x-axis and then I'd call it again for the y-axis (0,1,0). Unfortunately this function isn't working for me and I'm receiving unexpected results.

  2. I also tried the toxiclibs Quaternion object using createFromAxisAngle() to generate my two quaternions. However - I can't seem to find a way to apply this quaternion to a Vec3D...


Is there a better way to go about doing this? Is there a way to apply the quat to a PVector or Vec3D?

Answers

  • The way I am calling this function is: rotate( myVec, new PVector( 1, 0, 0 ), myAngle). I'm using the r argument to say that this is the rotation in the x-axis and then I'd call it again for the y-axis (0,1,0). Unfortunately this function isn't working for me and I'm receiving unexpected results.

    If you have a vector and apply a rotation about the X axis followed by a rotation about the Y axis the resultant vector will also show a rotation about the Z axis.

    So the function is probably working correctly and it is likely that the results you desired/expected are invalid.

    The problem is when you do the first rotation about the X axis you are also rotating the local coordinate system so that now the local Y axis is not the same as the global Y axis. When you then rotate about the Y axis you are rotating about the local axis. It means that the original vector has been rotated about the global Z axis.

  • edited July 2014

    Quark - thanks so much for the response. I should have been clearer about how this function isn't working for me. When using the rotate() function on the y-axis ONLY - I get very peculiar results (the line stretching off the screen). A rotation in the y-axis of the local vector shouldn't move it at all (since it would just be spinning cylindrically around its own direction.

    Below is my code:

    PVector start, end;
    PVector dir;
    
    void setup(){
      size(600,600, P3D);
      background(255);
    
      dir = new PVector(45, 60, 10);
      start = new PVector(width/2, height/2, 0);
      end = start.get();
      end.add( dir );
    
      stroke(0);
      line(start.x, start.y, start.z, end.x, end.y, end.z);
    
      PVector next = end.get();
      next.add( dir ); //grow line in direction of first line
      next = rotateQ(next, new PVector(0,1,0), 145); //apply rotation on y-axis
    
      stroke(255,0,0);
      line(end.x, end.y, end.z, next.x, next.y, next.z);
    }
    

    Do you see any reason why the rotate function is having any effect?

    Once I get this function behaving as I expect it should (where it actually isn't changing the vector at all in the above code example) - then I plan to add the x-axis rotation. At this point - your comment/note will need to be addressed where I'll want to rotate around the vectors original local axis - not its new translated one. Do you have any ideas on how to resolve this?

  • I can see where you are going wrong it is in lines 16 - 18 change them to

    PVector next = dir.get();
    next = rotateQ(next, new PVector(0, 1, 0), 145.0); //apply rotation on y-axis
    next.add( end ); //grow line in direction of first line
    

    when you rotate a PVector it is ALWAYS rotated about the origin e.g. [0,0,0]

    If you have a line from V1 to V2 that you want to rotate about the point V1 then you need to rotate(V2 - V1) + V1

    In my code

    line 1 is equivalent to (V2 - V1)

    line 2 is does rotate(V2 - V1)

    line 3 is does rotate(V2 - V1) + V1

    Also in 2D the origin is the top left corner of the display and in 3D it is normally in the middle of the display. Rather than adjusting the coordinates of all the PVectors it is better to translate the display matrix to move the origin to the centre.

    Putting these things together you get

    PVector start, end;
    PVector dir;
    
    void setup() {
      size(600, 600, P3D);
      background(255);
      translate(width/2, height/2); // Move origin to screen centre
    
      dir = new PVector(45, 60, 10);
      start = new PVector(); // starts at screen centre
      end = start.get();
      end.add( dir );
    
      stroke(0);
      line(start.x, start.y, start.z, end.x, end.y, end.z);
    
      PVector next = dir.get();
      next = rotateQ(next, new PVector(0, 1, 0), 145.0); //apply rotation on y-axis
      next.add( end ); //grow line in direction of first line
    
      stroke(255, 0, 0);
      line(end.x, end.y, end.z, next.x, next.y, next.z);
    }
    

    I should also point out the Quaternion class code is very limited and it will prove difficult to use once you get past basic rotations. There is a more advanced alternative but as with anything more advanced slightly harder to get your head round.

    If you are interested in knowing more let me know.

  • edited July 2014

    Quark - thanks again for taking the time to respond. Your explanation makes sense. I'm noticing the code you presented has two issues however:

    1. The second line drawn (from PVectors 'end' to 'next') - is abnormally long. It should be the same length of the first line. It seems the rotateQ function is extending the lines length. Why is this?

    2. If I switch the axis argument in the rotate function to the x-axis (1,0,0) - the line looks the same as when passing it the y-axis. The x axis of the direction vector should bend the line forward and backwards from the screen.

      PVector next = dir.get();
      next = rotateQ(next, new PVector(1, 0, 0), 145.0); //apply rotation on y-axis
      next.add( end ); //grow line in direction of first line
      
  • edited July 2014

    Some BAD news

    I have run some simple tests and there is either a problem with the Quaternion class or the rotateQ method - they don't work which is why you got unexpected results.

    More BAD news

    The maths of working with quaternions is beyond me so I can't fix the class or method.

    Now the GOOD news

    There is an Apache project called 'The Apache Commons Mathematics Library' and it has some great classes for handling rotations using quaternions. It is used internally by the PeasyCam library and by my Shapes3D library.

    I suggest that you use the Shapes3D library because I have modified the classes to use Processing's PVector class.

    You can install Shapes3D using the Sketch>Import Library>Add Library menu item.

    I have rewritten your code to make use of this library.

    import shapes3d.utils.*;
    import shapes3d.animation.*;
    import shapes3d.*;
    
    Rot rot;
    PVector dir, start, end, next;
    
    public void setup(){
      size(600,600,P3D);
      translate(width/2,height/2);
      background(255);
    
      // Define a rotation about the Y axis of 145 degrees
      rot = new Rot(new PVector(0,1,0), radians(145));
    
      dir = new PVector(45, 60, 10);
      // Initialise first line
      start = new PVector();
      end = start.get();
      end.add( dir );
      rot.applyTo(end);
    
      // draw firts bit of line
      stroke(0);
      line(start.x, start.y, start.z, end.x, end.y, end.z);
    
      // now for the next bit
      next = dir.get();
      rot.applyTo(next);
      next.add(end);
    
      stroke(255, 0, 0);
      line(end.x, end.y, end.z, next.x, next.y, next.z);
    }
    

    Note that we create a Rot object that defines the rotation we want to apply, it can then be used on as many PVectors as we wish. :)

    The Rot class is the key class and has 6 constructors that allow you to create rotations based on different initial information. In the code above we are specifying the rotation axis and the angle.

    Once you have tried it out I am sure you will have more questions.

    I should warn you that this library does not work in JavaScript mode.

  • Whewww... I'm glad I wasn't going crazy. I also don't understand the math in that rotate function. I am happy to use the shapes3D library and have no need for javascript mode. With your example code - I am wondering why you applied a rotation to the initial end? I believe a better 1-to-1 comparison would be:

    import shapes3d.utils.*;
    import shapes3d.animation.*;
    import shapes3d.*;
    
    Rot rot;
    PVector dir, start, end, next;
    
    public void setup(){
      size(600,600,P3D);
      translate(width/2,height/2);
      background(255);
    
      dir = new PVector(45, 60, 10);
      // Initialise first line
      start = new PVector();
      end = start.get();
      end.add( dir );
    
      // draw firts bit of line
      stroke(0);
      line(start.x, start.y, start.z, end.x, end.y, end.z);
    
      // Define a rotation about the Y axis of 145 degrees 
      rot = new Rot(new PVector(0,1,0), radians(145));
    
      // now for the next bit
      next = dir.get();
      rot.applyTo(next);
      next.add(end);
    
      stroke(255, 0, 0);
      line(end.x, end.y, end.z, next.x, next.y, next.z);
    }
    

    It's not stretching the line so that's great! - But, it is rotating the line when it really shouldn't be (because I'm only rotating the y-axis at this point). It seems like it may not be taking into account the local axis of the PVector next. Any thoughts on this?

    AND THANKS AGAIN FOR TAKING THE TIME! It's really nice to be validated regarding that quat class...

  • I applied the rotation to the initial end because in your comments you said

    //grow line in direction of first line

    and I took that as 'stretching' the line and I see that is not what you meant.

    But, it is rotating the line when it really shouldn't be (because I'm only rotating the y-axis at this point). It seems like it may not be taking into account the local axis of the PVector next. Any thoughts on this?

    Not sure what you mean the code above produces

    a black line that goes from [0,0,0] to [45, 60, 10]

    and a red line which is

    a red line that started from [0,0,0] to [45, 60, 10], then rotated 145 degrees about the global Y axis, then translated (moved) by [45, 60, 10]

    The following code I have taken the sketch and added PeasyCam and added the global axes. Try it out might help with visualising whats happening.

  • Understood on the first point. Regarding the second point - I think the misunderstanding is that I'm trying to rotate the red line around it's local y-axis, not the global y-axis.... Sorry if I didn't make that clear...

  • My original comments regarding global / local coordinates although valid were not applicable to your original problem because it turned out to be errors in the quaternion class and/or the rotate method.

    I am a little confused because a PVector can represent a point or a direction inside a coordinate system, they do not really have a local coordinate system. Let me give an example of what I mean by local coordinates.

    Consider a cube, to draw it we would need to know the XYZ coordinates of the 8 corners. These could be specified in global coordinates but it would mean that the coordinates would have to be changed whenever the cube is moved in global space. The solution is to define a local coordinate system where the origin is at the centre of the cube and the axis are parrallel with the global axes.

    So if the cube is centred about the global origin, then the global and local axes coincide. So what happens if we rotate the cube 45 degrees about the Z axis. Before the rotation the local axes in global space were

    X 1,0,0

    Y 0,1,0

    Z 0,0,1

    After the rotation the local axes in global space are

    X 1,-1,0

    Y 1,1,0

    Z 0,0,1 (unchanged because rotation is about this axis)

    So the question is, if we now apply a rotation about the X axis then the result will depend on whether we use the global or local X axis ~X(

    If it was the local axis then the local X axis would be unchanged but if the global axis then the local X axis would be changed.

    Normally it will be the global axis. To provide rotations about the local axis is much more challenging.

    It would help me if you describe what you are hoping to achive.

Sign In or Register to comment.