Rotate around objects local coordinate system

edited February 2014 in How To...

Hi! I'm trying to rotate a 3d object (just a square plane) around ITS coordinate system, and not Processings global coordinate system. When using rotateX(), rotateY(), and rotateZ(), the rotation is always around Processings origo. I want to do a rotation around x at first, which is fine to use rotateX since it's the first rotation, and then rotate around the objects new y-axis. In other words, I want the rotation to always be around the object itself. So imagine there being a coordinate system on the square plane with z-axis orthogonal against the plane, and the x- and y-axes in the plane (parallel).

I tried rotating around x first with rotateX, and then calculate a new y-axis (variable called new_vy) by multiplying the vector vy = {0, 1, 0} by x's rotational matrix, and then use rotate(angle, new_vy[0], new_vy[1], new_vy[2]);

Some semi-psuedocode:

//Code begin
rotateX(angleX);

new_vy = Rx(angleX)*vy;           //Rx is the rotational matrix for x-rotation. Note that this is a matrix operation,
                                                  //and the actual code doesn't look like this.

draw3dobject();

//Code end

This should be done for z aswell, but x and y is enough for starters.

So, any suggestions? Doesn't have to be a modification to my code, can be an entirely different method.

I've found something about quaternions, but I would prefer not to use them, since I don't entirely understand them, and I think that this should work with out them.

/Daniel

Tagged:

Answers

  • Similar to earth orbiting around the sun? Is that what you're trying to accomplish?

  • edited February 2014

    Processing's rotation functions are based on the origin (rotation functions in general are). To do rotations as you described: x-axis first, then new y-axis / z-axis, etc. you would just translate to the center of your plane first and then apply rotations.

    Edit: This may require matrices for the translations and rotations, sorry this was an important detail to leave out. If the answer I added below doesn't solve the problem that might be what you are looking for

  • If what you're looking for is what's being described by Asimes, here's a sample code:

    float num;
    void setup()
    {
      size(600, 600, P3D);
      noStroke();
    }
    
    void draw()
    {
      background(255);
      smooth(8);
      lights();
    
      pushMatrix();
      translate(width/2, height/2);
      fill(#999999);
      sphere(50);
      popMatrix();
    
      pushMatrix();
      translate(width/2, height/2);
      rotateX(num);
      translate(0,200);
      fill(#ff0000);
      box(20);
      popMatrix();
    
      pushMatrix();
      translate(width/2, height/2);
      rotateY(num);
      translate(200,0);
      fill(#00ff00);
      box(20);
      popMatrix();
    
      num += 0.05;
    }
    
  • Well I appriciate your answers, but it's not quite what I was looking for. asimes might be on to something, but that's kindof what I've already tried. But I guess something might be incorrect with my code.

    I made a picture to explain what I mean: http://postimg.org/image/5blmdo6bn/

    The translation isn't really a part of the task, the objects origin and Processings origin might as well coincide. In the end though, as a final step, I want to translate the object to the window center for viewing purposes, it's a bit difficult to view the entire object when it's located in the top left corner.

  • This example does (I think) what you want, but it uses a built in Processing object which by default is at the origin. Can you post some code that represents your 3D objects?

    void setup() {
      size(600, 600, P3D);
      noFill();
    }
    
    void draw() {
      background(255);
      translate(width/2, height/2);
    
      rotateZ(TWO_PI/16);
      rotateY(radians(frameCount));
      line(0, -100, 0, 0, 100, 0);
      box(100);
    }
    
  • Sorry guys, still not what I'm looking for. I think my picture explains it pretty, good; I want to rotate the object around ITS coordinate system, and not the global. The code you posted asimes doesn't rotate around the box's coordinate system, at least not for the second rotation. I modified it, to maybe explain what I'm after:

    float angX;
    float angY;
    
    
    void setup() {
      size(600, 600, P3D);
      noFill();
    }
    
    void draw() {
      angX = (mouseY - height/2)*2.0f*PI/height;
      angY = (mouseX - width/2)*2.0f*PI/width;
      background(255);
      translate(width/2, height/2);
    
      rotateZ(angX);
      rotateY(angY);
      stroke(0, 244, 100);
      line(0, -100, 0, 0, 100, 0);
      stroke(244, 0, 100);
      line(0, 0, -100, 0, 0, 100);
      stroke(0);
      line(-100, 0, 0, 100, 0, 0);
      stroke(0);
      box(100);
    }
    

    What I want is to rotate the box around the lines going through it (i.e. its own coordinate system), and not the global coordinate system. When you drag the mouse right and left, it's all good since it's the first rotation. But when dragging it up and down, it rotates around the axis perpendicular to the computer screen. The behavior I'm after does the second rotation of the box around some of the lines in it.

  • Actually it does rotate around the box's coordinate system. The reason it does it because the box is centered at the origin and the orientation of its coordinate system is the same as the global one.

    In my code:

    rotateZ(TWO_PI/16);

    Changes the global coordinate system, which currently is the same as the box's coordinate system. This changes the x-axis and y-axis which means the next rotation:

    rotateY(radians(frameCount));

    Is not world aligned. That's why the box spins on a tilt in the sketch.

    If you wanted to think about it as if it were the "box"'s coordinate system, then just put pushMatrix() and popMatrix() before and after the code. If the box is not supposed to be centered at the origin then also put a translate() within the matrix stack.

  • edited February 2014

    I was trying to do the exact same thing. I have this code lying around. you need toxiclibs and peasycam library. Use quaternion to repesent orientation. Then multiply that with new quaternion to create a new orientation relative to old one. W,S,A,D,Q,E buttons to control the plane.

    import toxi.geom.*;
    import toxi.processing.*;
    import peasy.*;
    
    PeasyCam cam;
    Quaternion q;
    
    PVector loc;
    
    void setup() {
      size(850, 850, P3D); 
      smooth(16);
      cam = new PeasyCam(this, defaultCam[3]);
      cam.setMinimumDistance(0.00001);
      cam.setMaximumDistance(999999999);
      cam.setRotations(defaultCam[0], defaultCam[1], defaultCam[2]);
      cam.lookAt(0, 0, 0);
      //
      q = new Quaternion();
      loc = new PVector(0, 0, 0);
    } // end of setup
    
    void draw() {
      background(18, 18, 20);
      // set clipping distance
      float cameraZ = ((height/2.0) / tan(PI*60.0/360.0));
      perspective(PI/3.0, (float) width/height, cameraZ/100.0, cameraZ*100.0);
      //HUD
      cam.beginHUD();
      fill(255);
      textAlign(LEFT);
      text((int)frameRate, 5, 15);
      cam.endHUD();
    
      visualGuide();
      //
    
      float o[] = q.toAxisAngle();
    
      lights();
      pushMatrix();
      stroke(0);
      fill(255);
      rotate(o[0], o[1], o[2], o[3]);
      translate(0, 0, 100);
      box(10, 10, 10);
      float headingX = modelX(0, 0, 0);
      float headingY = modelY(0, 0, 0);
      float headingZ = modelZ(0, 0, 0);
      popMatrix();
    
      stroke(255);
      line(0, 0, 0, headingX, headingY, headingZ);
      PVector vel = new PVector(headingX, headingY, headingZ);
      vel.normalize();
      vel.mult(1);
      loc.add(vel);
    
      pushMatrix();
      stroke(0);
      fill(255);
      translate(loc.x, loc.y, loc.z);
      rotate(o[0], o[1], o[2], o[3]);
      box(10, 10, 100);
      box(50, 10, 10);
      popMatrix();
    } // end of draw
    
    
    void pitchUP() {
      Quaternion temp = Quaternion.createFromEuler(radians(0), radians(0), radians(5));
      q = q.multiply(temp);
    }
    void pitchDOWN() {
      Quaternion temp = Quaternion.createFromEuler(radians(0), radians(0), radians(-5));
      q = q.multiply(temp);
    }
    void rollLEFT() {
      Quaternion temp = Quaternion.createFromEuler(radians(0), radians(5), radians(0));
      q = q.multiply(temp);
    }
    void rollRIGHT() {
      Quaternion temp = Quaternion.createFromEuler(radians(0), radians(-5), radians(0));
      q = q.multiply(temp);
    }
    void yawLEFT() {
      Quaternion temp = Quaternion.createFromEuler(radians(5), radians(0), radians(0));
      q = q.multiply(temp);
    }
    void yawRIGHT() {
      Quaternion temp = Quaternion.createFromEuler(radians(-5), radians(0), radians(0));
      q = q.multiply(temp);
    }
    
    
    double[] defaultCam = {
      2.5, 0, 3.14, 300
    };
    boolean enableAxis = true, 
    enableGrid = true;
    
    void keyPressed() {
      if ( key == 'z' || key == 'Z') {
        cam.setRotations(defaultCam[0], defaultCam[1], defaultCam[2]);
        cam.lookAt(0, 0, 0); 
        cam.setDistance(defaultCam[3]);
      }
      else if ( key == 'x' || key == 'X') {
        enableAxis = !enableAxis;
      }
      else if ( key == 'c' || key == 'C') {
        enableGrid = !enableGrid;
      }
    
      if (key == 'w' || key == 'W') {
        pitchUP();
      }
      if (key == 's' || key == 'S') {
        pitchDOWN();
      }
      if (key == 'a' || key == 'A') {
        rollLEFT();
      }
      if (key == 'd' || key == 'D') {
        rollRIGHT();
      }
      if (key == 'q' || key == 'Q') {
        yawLEFT();
      }
      if (key == 'e' || key == 'E') {
        yawRIGHT();
      }
    }
    
    void visualGuide() {
      //// world AXIS
      if (enableAxis) {
        noFill();
        strokeWeight(1);
        stroke(0, 255, 0, 128);
        line(0, 0, 0, 0, 80, 0); // y
        stroke(255, 0, 0, 128);
        line(0, 0, 0, 80, 0, 0);  // x
        stroke(0, 0, 255, 128);
        line(0, 0, 0, 0, 0, 80);  // z
        stroke(255, 50);
        box(10);
        stroke(255);
      }
      //// world GRID
      if (enableGrid) {
        stroke(255, 80);
        for (int i = 0; i < 10; i++) {
          int spc = i*50;
          pushMatrix();
          translate(-450/2, 0, -450/2);
          line(spc, 0, 0, spc, 0, 450);  // z
          line(0, 0, spc, 450, 0, spc);  // x
          popMatrix();
        }
        stroke(255);
      }
    }
    
    void mouseClicked() {
      //println(cam.getRotations());
    }
    
  • edited February 2014

    In the sketch below if you move the mouse up from the center and then left or right you can see the box rotating around the dark green line. This is a x-axis rotation followed by a y-axis rotation where the dark green line is the "new" y-axis. I believe this is the result that you want.

    However, if you move the mouse right from the center and then up or down, you still are doing a x-axis rotation followed by a y-axis rotation because that is how your code is set up:

    rotateX(angX); 
    rotateY(angY);
    

    Moving the mouse right first doesn't change the order that you are doing rotations. My sketch does not solve your problem, but hopefully it makes it a little more clear what is going on. kn1c posted about quaternions, that may be a solution. Really what you need to look into if not those are how to do rotations without Processing functions, the order of rotations will always matter for those.

    float angX, angY;
    
    void setup() {
      size(640, 640, P3D);
      noFill();
    }
    
    void draw() {
      background(255);
      translate(width/2, height/2);
    
      angX = (mouseY-height/2)*TWO_PI/height;
      angY = (mouseX-width/2)*TWO_PI/width;
    
      // Global coordinate system
      stroke(255, 128, 128);
      line(0, 0, 0, 100, 0, 0); // x
      stroke(128, 255, 128);
      line(0, 0, 0, 0, 100, 0); // y
      stroke(128, 128, 255);
      line(0, 0, 0, 0, 0, 100); // z
    
      // "Save" initial coordinate system
      pushMatrix();
      rotateX(-angX);
      rotateY(angY);
    
      // Box's coordinate system
      stroke(128, 0, 0);
      line(0, 0, 0, 100, 0, 0); // x
      stroke(0, 128, 0);
      line(0, 0, 0, 0, 100, 0); // y
      stroke(0, 0, 128);
      line(0, 0, 0, 0, 0, 100); // z
    
      stroke(0);
      box(100);
    
      // Global coordinate system unaffected after popMatrix()
      popMatrix();
    
      // If you had other objects here they would be world aligned again
    }
    

    Edit: Dannjell had emailed me code, I posted my response here as this problem comes up on the forum occasionally

  • edited February 2014

    I think that kn1c's code solved it for me. Many thanks! I guess quaternions are the only alternative :-P But thank you everyone who provided answers and help!

    Edit: How do I mark this question as 'Answered'?

Sign In or Register to comment.