How to find the rotation of the entity?

I want to find the rotation of an entity around an axis.

I am using following piece of code

    e = Sketchup.active_model.entities[0]
    t = e.transformation
    a = t.to_a
    rotX = Math.acos(a[5]).radians

a) Initial State


Return the rotX as 180 degrees

b) Now rotate it anti-clockwise direction around red axis by 45


Return the rotX as 135 degrees

c) Now rotate it clockwise direction around red axis by 45


Return the rotX as 135 degress.

What am I doing wrong here ? Why rotation returned is same in both cases ?

1 Like

You can’t tell the direction of a rotation by looking just at that one element of the matrix because cos(x) = cos(-x).

2 Likes

The documentation says that the acos function returns a positive angle between 0 and 180 degrees.

image

You might want to try using atan2 instead; it returns a positive or negative value between -180 and 180.

image

Also note that the asin function ranges from -90 to 90 and that sin/cos = tan.

2 Likes

I’m working on a library that among other things can find the Euler angles for a transformation.

The library isn’t released yet but maybe it can still be of use (I’m using it myself in my upcoming extension).

2 Likes

Keep in mind that this problem has many solutions. A transformation matrix has no notion of rotation or direction. It depends on the choice of axes and the order in which rotations are applied (except if you have a simple case or constraints, like a given axes or a single angle).
The most common expectation is to extract angles in the order of Euler angles and in the direction of the smaller angle (< 180°).

This is a problem that many SketchUp Ruby developers have already worked with, myself included (see here). It’s great to have a shared library.

I’m sure this later one is much improved, but FYI, it’s almost ten years since I did this code below, as part of a bigger example over on SketchUcation.com
I added modules etc to tidy it up here…
You pass a transformation tr with 0.1.2 or x,y,z axes…
The # sections in the rot’ methods are older alternatives that will sometimes not give the same results…
Usage example:
rotx = TIG::Transformation.rotX(transformation)

module TIG
module Transformation
 def self.euler_angle(tr, xyz=[])
  m = tr.xaxis.to_a + tr.yaxis.to_a + tr.zaxis.to_a
  if m[6] != 1 && m[6] != -1
    ry = -Math.asin(m[6])
    rx = Math.atan2(m[7]/Math.cos(ry), m[8]/Math.cos(ry))
    rz = Math.atan2(m[3]/Math.cos(ry), m[0]/Math.cos(ry))
  else
    rz = 0
    phi = Math.atan2(m[1], m[2])
    if m[6] == -1
      ry = Math::PI/2
      rx = rz + phi
    else
      ry = -Math::PI/2
      rx = -rz + phi
    end
  end   
  return -rx if xyz==0
  return -ry if xyz==1
  return -rz if xyz==2
  return [-rx,-ry,-rz] if xyz==[]
 end
 ###
 def self.rotX(tr)
  #(Math.atan2(self.to_a[9],self.to_a[10]))
  #Math.acos(self.to_a[5])
  self.euler_angle(tr, 0)
 end
 def self.rotY(tr)
  #(Math.arcsin(self.to_a[8]))
  #Math.acos(self.to_a[0])
  self.euler_angle(tr, 1)
 end
 def self.rotZ(tr)
  #(-Math.atan2(self.to_a[4],self.to_a[0]))
  #Math.asin(self.to_a[4])
  self.euler_angle(tr, 2)
 end
 def self.rotXYZ(tr)
  self.euler_angle(tr)
 end
end
end
1 Like

Thanks for sharing, it’s certainly helpful and inspiring to compare with other implementations.

On a side-note, I personally favor having no more than a single possible return type (type safety) instead of a control parameter (control coupling). That makes code easier to maintain and refactor. The full vector is computed anyways and its components can be accessed with .x accessor methods.

I think this was one of the implementations I looked at when making my own. Aerilius also have an implementation is closer related to. They are both based on the same paper (that I sadly forgot to add as a comment).

Thanks guys for sharing your implementation.
I did mange to find angle in some what different way - using CSC 418: Geometric Transformations: Derivation

Assuming no shearing or scaling

def rot(tr)
   a = tr.to_a
   x = Math.atan2(a[6],a[10]).radians
   y = Math.atan2(a[8],a[10]).radians
   z = Math.atan2(a[1],a[5]).radians
   [x, y, z]
end

Am I missing/assuming something here ?

Perhaps a tiny detail but I’d prefer having the method return angles in radians, as that is what is used in other geometric functions, both in the API and Ruby core. Converting to degrees can be done in the display layer using Sketchup.format_angle, which uses the system decimal separator and model angle precision.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.