How extracting only rotation angles from a Matrix?

edited March 2018 in How To...

Hi all, Does anybody know how to extract rotation angles from a Matrix, like for position with the functions modelX, Y, Z()? I think I already know about getMatrix(), setMatrix() or applyMatrix(), but I need to get individually translation and rotation informations after several cumulative Matrix transformations. Especially because both set/applyMatrix() have no effect with OBJexport (for after boolean operations in Blender in my case..), but I also believe it could be very usefull and interesting. I don't well understand Matrix inside informations... Hope someone can help, thanks!

Answers

  • edited March 2018 Answer ✓

    Hi step, I referred to this Stack Overflow discussion, then tried this:

    PMatrix3D m = new PMatrix3D();
    PMatrix3D n = new PMatrix3D();
    
    PShape a;
    PShape b;
    
    void setup() {
      size(512, 256, P3D);
      surface.setLocation(10, 10);
      float[] original = { random(-PI, PI), random(-PI, PI), random(-PI, PI) };
      float[] extracted = new float[3];
    
      m.rotateX(original[0]);
      m.rotateY(original[1]);
      m.rotateZ(original[2]);
    
      println("ORIGINAL");
      m.print();
      println(original[0], original[1], original[2]);
    
      euler(m, extracted);
    
      n.rotateX(extracted[0]);
      n.rotateY(extracted[1]);
      n.rotateZ(extracted[2]);
    
      println("\nEXTRACTED");
      n.print();
      println(extracted[0], extracted[1], extracted[2]);
    
      a = createShape(BOX, 1);
      a.setStrokeWeight(0);
      a.setFill(0xffff7f00);
      a.applyMatrix(m);
      a.scale(64);
      a.translate(width * .25, height * .5);
    
      b = createShape(BOX, 1);
      b.setStrokeWeight(0);
      b.setFill(0xff007fff);
      b.applyMatrix(n);
      b.scale(64);
      b.translate(width * .75, height * .5);
    }
    
    void draw() {
      background(0xffffffff);
      ortho();
      directionalLight(255, 255, 255, 0, 0.6, -0.8);
      shape(a);
      shape(b);
    }
    
    float[] euler(PMatrix3D m) {
      return euler(m, new float[3]);
    }
    
    float[] euler(PMatrix3D m, float[] out) {
      // y-axis is inverted in Processing.
      out[0] = atan2(-m.m12, m.m22);
      out[1] = atan2(m.m02, sqrt(pow(m.m12, 2.0) + pow(m.m22, 2.0)));
      out[2] = atan2(-m.m01, m.m00);
      return out;
    }
    

    Euclidean Spaces also has an explanation of this and other conversions. I've not tested the following, but I wonder if you'll want to check that the rotation mode of an object (XYZ in the above example) is the same in Processing as in Blender.

  • That works perfectly! Thank you so much!

    Checking rotation modes between Processing and Blender, XYZ axes are for me all the sames (hope I don't make any mistake..). With this precision : importing an .obj file exported from processing with OBJexport in Blender, applies a 90° rotation on X axis in the transform panel. That can be erased.

  • edited April 2018

    But I really feel unsure with my previous answer cause, comparing visually axes orientations between Processing and Blender, X & Y axes have permuted (90° X rotation canceled)... Do we have to consider separately Processing & OBJexport in comparison with Blender, for rotation mode?

  • edited April 2018 Answer ✓

    Well, I guess I'd start by giving myself some diagnostic tools. First, I'd draw the transform in Processing...

    void drawTransform(float len, float strwgt0, float strwgt1) {
      pushStyle();
    
      // Right
      strokeWeight(strwgt0);
      stroke(0xffff0000);    
      line(0.0, 0.0, 0.0, len, 0.0, 0.0);
      strokeWeight(strwgt1);
      point(len, 0.0, 0.0);
    
      // Up
      strokeWeight(strwgt0);
      stroke(0xff00ff00);    
      line(0.0, 0.0, 0.0, 0.0, len, 0.0);
      strokeWeight(strwgt1);
      point(0.0, len, 0.0);
    
      // Forward
      strokeWeight(strwgt0);
      stroke(0xff0000ff);    
      line(0.0, 0.0, 0.0, 0.0, 0.0, len);
      strokeWeight(strwgt1);
      point(0.0, 0.0, len);
    
      popStyle();
    }
    

    Then, I'd take a screen shot every time I saved an obj file.

    import nervoussystem.obj.OBJExport;
    
    boolean record = false;
    String filepath = "results/";
    String filename = "example";
    String objext = ".obj";
    String imgext = ".png";
    String objfileresult = "";
    
    void setup() {
      size(512, 256, P3D);
      noStroke();
    }
    
    void draw() {
      background(0xffffffff);
      directionalLight(255, 255, 255, 0.0, 0.6, -0.8);
    
      if (record) {
        beginRecord("nervoussystem.obj.OBJExport", objfileresult);
      }
    
      pushMatrix();
      translate(width * 0.5, height * 0.5, 0.0);
      rotateY(frameCount * 0.01);
      scale(50.0);
    
      drawTransform(1.25, 0.025, 0.125);
      fill(0xffff7f00);
      box(1.0);
    
      pushMatrix();
      translate(2.0, 0.0, 0.0);
      rotateX(frameCount * 0.01);
      scale(0.85);
    
      drawTransform(1.25, 0.025, 0.125);
      fill(0xff007fff);
      box(1.0);
    
      pushMatrix();
      translate(0.0, 2.0, 0.0);
      rotateZ(frameCount * 0.01);
      scale(0.85);
    
      drawTransform(1.25, 0.025, 0.125);
      fill(0xffff007f);
      box(1.0);
    
      popMatrix();
      popMatrix();
      popMatrix();
    
      if (record) {
        endRecord();
        println(objfileresult, "saved");
        record = false;
      }
    }
    

    This is so I can compare the screenshot against my result in Blender.

    In Processing Link

    In Blender Link

    The world coordinates in Blender and Processing are very different. In Processing the y-axis which describes up is flipped, so (0.0, 1.0, 0.0) moves down and (0.0, -1.0, 0.0) moves up; in Blender the y-axis describes forward while the z-axis describes up.

    Furthermore, in Blender there's a distinction between translating, rotating and scaling an object while in Object Mode and doing the same to vertices, edges and faces in Edit Mode.

    In Processing, that distinction is less clear and harder to control, but I imagine the difference in world coordinates would impact both your object's orientation and that of its vertices. Another wrinkle is how you export the boxes, in the above example, I've exported all three boxes as one object. In Blender I could enter edit mode, select the desired faces, then create separate objects.

  • edited April 2018

    Hi behreajj,

    Thank you for your answer, all these precisions and references. It has required some time to me for understanding, doing some tests, and finally it's still a work in progress :)

    Well, I first compare axes between Processing/OBJexport and Blender using your drawTransform function :

    In Processing :

    114116

    In Blender :

    BlenderAxes

    Boxes lengths increase from X to Z, and as I can retrieve same axes I believe (and hope so) it doesn't a matter for me, except that Z is inverted...

    Your first code example gave me the idea that storing all matrix transformations in a list was probably the only solution that could finally solve my problem :

    void transrot(float xtrans, float ytrans, float ztrans, float xrot, float yrot, float zrot) {
      transrot.append(xtrans);
      transrot.append(ytrans);
      transrot.append(ztrans);
      transrot.append(xrot);
      transrot.append(yrot);
      transrot.append(zrot);
      //println(transrot);
      translate(xtrans, ytrans, ztrans);
      rotateX(xrot);
      rotateY(yrot);
      rotateZ(zrot);
    }
    
    void trRestore() {
      for (int i = 0; i < transrot.size()/6; i+=1) {
        translate(transrot.get(i*6), transrot.get(i*6 + 1), transrot.get(i*6 + 2));
        rotateX(transrot.get(i*6 + 3));
        rotateY(transrot.get(i*6 + 4));
        rotateZ(transrot.get(i*6 + 5));
        /*
         println(i);
         print(transrot.get(i*6) + "  ");
         print(transrot.get(i*6 + 1) + "  ");
         print(transrot.get(i*6 + 2) + "  ");
         print(transrot.get(i*6 + 3) + "  ");
         print(transrot.get(i*6 + 4) + "  ");
         print(transrot.get(i*6 + 5) + "  ");
         println();
         */
      }
    }
    

    Transformations history must be restored as needed. I probably was afraid to play with different matrix like in your example, because that's really so hot sometimes with just only one! I don't know yet if there are any solution that way.

    Finally, here is a full code example :

    import nervoussystem.obj.*;
    
    FloatList transrot;
    
    void setup() {
      size(500, 500, P3D);
      transrot = new FloatList();
      frameRate(1);
    }
    
    boolean record = false;
    int a = 25;
    
    void draw() {
      background(0);
      stroke(255);
      ortho();
      lights();
    
      if (!record) {
        translate(width/2, height/2, 0);
      }
    
      if (record) {
        beginRecord("nervoussystem.obj.OBJExport", "cub.obj");
      }
      transrot(random(-a, a), random(-a, a), random(-a, a), random(-PI, PI), random(-PI, PI), random(-PI, PI));
      transrot(random(-a, a), random(-a, a), random(-a, a), random(-PI, PI), random(-PI, PI), random(-PI, PI));
      transrot(random(-a, a), random(-a, a), random(-a, a), random(-PI, PI), random(-PI, PI), random(-PI, PI));
      noFill();
      stroke(255, 0, 0);
      box(20);
      drawTransform(40, 1, 5);
      if (record) {
        endRecord();
      }
    
      for (int i = 0; i < 15; i++) {
    
        pushMatrix(); //
        translate(10*i, 0, 0);
        rotateX(TWO_PI/10*i);
        rotateY(TWO_PI/10*i);
        rotateZ(TWO_PI/10*i);
        fill(0, 255, 0);
        noStroke();
        box(10);
        popMatrix(); //
    
        println(transrot.size() + " -> " +transrot.size()/6);
    
        if (record) {
          pushMatrix(); //
          beginRecord("nervoussystem.obj.OBJExport", "prim/prim" + nf(i, 2) + ".obj");
          trRestore();
          translate(10*i, 0, 0);
          rotateX(TWO_PI/10*i);
          rotateY(TWO_PI/10*i);
          rotateZ(TWO_PI/10*i);
          noFill();
          stroke(0, 255, 0);
          box(10);
          endRecord();
          popMatrix(); //
        }
      }
    
      transrot.clear();
      println();
    
      record = false;
    }
    
    void keyPressed() {
      if (key == 's' || key == 'S') {
        save(millis()+".jpg");
      }
      if (key == 'r' || key == 'R') {
        record = true;
        //redraw();
      }
    }
    

    After many tests (and bugs...), it appears to me that it now works.

    In Blender :

    BlenderM

    Different matrix transformations can than be reproduced, a position with right axes rotations can be restored. That way multiple files can be exported separately, and boolean operations can than be applied between them in Blender.

    I hope it is not a dream (!), and that this method will work ALL the time!

    Feel really free to give me frankly your opinion! And many thanks again for your help!

Sign In or Register to comment.