• Login
sketchucation logo sketchucation
  • Login
โ„น๏ธ GoFundMe | Our friend Gus Robatto needs some help in a challenging time Learn More

Compute Rotation and Scale from Transform Object

Scheduled Pinned Locked Moved Developers' Forum
26 Posts 9 Posters 4.6k Views
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    dburdick
    last edited by 24 Mar 2010, 02:32

    @tig said:

    I think the bits of code that're commented out [starts with a #] are perhaps the right code ?
    Swap the two around - this was a work-in-progress that stalled...
    Try

    
    > def rotX
    >   Math.atan2(self.to_a[9],self.to_a[10])
    >   ###Math.acos(self.to_a[5]).radians
    > end
    

    ??????

    Let me know how it works...

    Hi TIG,

    Yes, when switching to the commented code versus the original lines it works better (doesn't bomb) - but it give incorrect results when two or more rotations are involved - e.g. roation around the X followed by rotation around Z. I'm going to try to see if I can return better results by working with 2 vectors intead of the whole matrix.

    1 Reply Last reply Reply Quote 0
    • D Offline
      dburdick
      last edited by 24 Mar 2010, 03:48

      I think I found a more foolproof way of doing this by using transform object axis vectors. It turns out, that the 4 x 4 matrix needs to be normalized first (e.g. sans any non-uniform scaling) before computing the rotation angles. So the way to do this is to use the already normalized axis vectors (x-axis, yaxis, etc.). So here's the revised code which returns the rotation angles in degrees:

        def rotX
           Math.atan2(self.yaxis.z,self.yaxis.y).radians
        end
        def rotY
      	Math.atan2(self.xaxis.z,self.xaxis.x).radians
        end
        def rotZ
      	Math.atan2(self.xaxis.y,self.xaxis.x).radians
        end
      

      Update: Sorry I jumped the gun here a bit - this isn't correct. The scaling issues now work but the multi rotation problem is still here

      1 Reply Last reply Reply Quote 0
      • D Offline
        dburdick
        last edited by 24 Mar 2010, 06:43

        Okay, I got it working to return nice clean Euler angles from a transform object. Here's the code (note - it returns right-handed angles in degrees):

        def euler
        	m = self.xaxis.to_a + self.yaxis.to_a + self.zaxis.to_a
        	if m[6] != 1 and 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.radians, -ry.radians, -rz.radians
        end
        
        1 Reply Last reply Reply Quote 0
        • T Offline
          TIG Moderator
          last edited by 24 Mar 2010, 09:56

          Thanks for the input...
          Here's my revised code using you ideas - it's still in radians and I have tried not to clash with your method naming etc - it now finds x/y/z rotations and [x,y,z] rotation as an array and has extra methods to set rotations from a 3 item array, referring either to the model axes or object's axes...

          ### transformation.extensions.rb (c) TIG 2009
          ### From original ideas by TBD and others ? ### TIG 20091010
          ### The euler rotation ideas from Dave Burdick 20100324
          ### It extends the methods for Geom;;Transformation...
          ### The built-in method "object.transformation.origin" return a point3d 
          ###   that is the object's origin/insertion; the new method 
          ###   object.transformation.getX etc returns the X location of the  
          ###   object [or Y or Z].
          ### The setX(x) resets the X value of the object [or Y or Z]; it 
          ###   returns a new transformation that can then be used to reset the 
          ###   original transformation; thus;
          ###   object.transformation=object.transformation.setX(another_object.transformation.getX)
          ###   - here it makes the object's X = another_object's X ; 
          ###     it also could be given a float, e.g. 12.345 or a 'variable'...
          ### The object.transformation.scaleX etc returns the scale on that axis.
          ### The object.transformation.rotX etc returns the rotation on that axis
          ###   in radians; use ... .rotX.radians to get it in degrees...
          ### It uses different names to TBD's, e.g. 'rotZ' instead of 'zrot' etc.
          ### Note the capitalization... this is because some 'compiled scripts' 
          ### use 'rotz' already etc - [and they return the rotation in degrees!]
          ### object.transformation.rotXYZ
          ###   returns a 3 item array giving the rotations in x/y/z
          ### object.transformation.rot_a
          ###   returns an 11 item array of the transformation's rotation/scaling 
          ###   - it can be used to extract some data more easily or as below...
          ### object.transformation=object.transformation.rotation_from_rot_a(another_object.transformation.rot_a) 
          ###   this applies another_object's 'rot_a' to change the object.
          ### object.transformation=object.transformation.rotation_from(another_object.transformation) 
          ###   this applies another_object's rotation/scaling to the object.
          ###   it returns a new transformation that can then be used to reset the original...
          ### object.transformation=object.transformation.origin_from(another_object.transformation) 
          ###   this applies another_object's origin/location to the object.
          ###   it returns a new transformation that can then be used to reset the original...
          ### object.transformation=object.transformation.rotation_from_xyz([xrot,yrot,zrot])
          ###   this applies a 3 item array of x/y/z rotations about the model's x/y/z-axes, 
          ###   this could also be the array returned by rotXYZ.
          ###   it returns a new transformation that can then be used to reset the original...
          ### object.transformation=object.transformation.rotation_from_xyz_locally([xrot,yrot,zrot])
          ###   this applies a 3 item array of x/y/z rotations about the objects's x/y/z-axes, 
          ###   this could also be the array returned by rotXYZ.
          ###   it returns a new transformation that can then be used to reset the original...
          ###
          class Geom;;Transformation
              def euler_angle(xyz=[])
                 m = self.xaxis.to_a + self.yaxis.to_a + self.zaxis.to_a
                 if m[6] != 1 and 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 getX
              self.to_a[12]
            end
            def getY
              self.to_a[13]
            end
            def getZ
              self.to_a[14]
            end
            def setX(x)
              if not x.class==Float and not x.class==Integer
                puts "Transformation;;setX( ) expects a Float or Integer."
                return nil
              end#if
              x=x.to_f
              t=self.to_a
              t[12]=x
              return self.set!(t)
            end
            def setY(y)
              if not y.class==Float and not y.class==Integer
                puts "Transformation;;setY( ) expects a Float or Integer."
                return nil
              end#if
              y=y.to_f
              t=self.to_a
              t[13]=y
              return self.set!(t)
            end
            def setZ(z)
              if not z.class==Float and not z.class==Integer
                puts "Transformation;;setZ( ) expects a Float or Integer."
                return nil
              end#if
              z=z.to_f
              t=self.to_a
              t[14]=z
              return self.set!(t)
            end
            def scaleX
              Math.sqrt(self.to_a[0]**2+self.to_a[1]**2+self.to_a[2]**2)
            end
            def scaleY
              Math.sqrt(self.to_a[4]**2+self.to_a[5]**2+self.to_a[6]**2)
            end
            def scaleZ
              Math.sqrt(self.to_a[8]**2+self.to_a[9]**2+self.to_a[10]**2)
            end
            def rotX
              #(Math.atan2(self.to_a[9],self.to_a[10]))
               #Math.acos(self.to_a[5])
               euler_angle(0)
            end
            def rotY
              #(Math.arcsin(self.to_a[8]))
              #Math.acos(self.to_a[0])
              euler_angle(1)
            end
            def rotZ
              #(-Math.atan2(self.to_a[4],self.to_a[0]))
              #Math.asin(self.to_a[4])
              euler_angle(2)
            end
            def rotXYZ
              euler_angle
            end
            def rot_a ### rotation matrix 4x3 3 and 7 are nil
              t=self.to_a
              r=[]
              [0,1,2,3,4,5,6,7,8,9,10].each{|i|r[i]=t[i]}
              r[3]=nil
              r[7]=nil
              return r
            end
            def rotation_from_xyz(xyz)
              if not xyz.class==Array and not xyz[2] and not xyz[0].class==Float and not xyz[1].class==Float and not xyz[2].class==Float
                puts "Transformation;;rotation_from_xyz( ) expects a 3 Item Array of Angles [as Floats]."
                return nil
              end#if
              tx=Geom;;Transformation.rotation(self.origin,X_AXIS,xyz[0])
              ty=Geom;;Transformation.rotation(self.origin,Y_AXIS,xyz[1])
              tz=Geom;;Transformation.rotation(self.origin,Z_AXIS,xyz[2])
              t=(tx*ty*tz)
              return self.set!(t)
            end
            def rotation_from_xyz_locally(xyz)
              if not xyz.class==Array and not xyz[2] and not xyz[0].class==Float and not xyz[1].class==Float and not xyz[2].class==Float
                puts "Transformation;;rotation_from_xyz_locally( ) expects a 3 Item Array of Angles [as Floats]."
                return nil
              end#if
              tx=Geom;;Transformation.rotation(self.origin,self.xaxis,xyz[0])
              ty=Geom;;Transformation.rotation(self.origin,self.yaxis,xyz[1])
              tz=Geom;;Transformation.rotation(self.origin,self.zaxis,xyz[2])
              t=(tx*ty*tz)
              return self.set!(t)
            end
            def rotation_from_rot_a(rot_a)
              if not rot_a.class==Array and not rot_a[10]
                puts "Transformation;;rotation_from_rot_a( ) expects an 11 Item Array."
                return nil
              end#if
              t=self.to_a
              [0,1,2,4,5,6,8,9,10].each{|i|t[i]=rot_a[i].to_f}
              return self.set!(t)
            end
            def rotation_from(trans)
              if not trans.class==Geom;;Transformation
                puts "Transformation;;rotation_from( ) expects a Sketchup;;Transformation."
                return nil
              end#if
              t=self.to_a
              tt=trans.to_a
              [0,1,2,4,5,6,8,9,10].each{|i|t[i]=tt[i].to_f}
              return self.set!(t)
            end
            def origin_from(trans)
              if not trans.class==Geom;;Transformation
                puts "Transformation;;origin_from( ) expects a Sketchup;;Transformation."
                return nil
              end#if
              t=self.to_a
              tt=trans.to_a
              [12,13,14].each{|i|t[i]=tt[i].to_f}
              return self.set!(t)
            end
          end#class
          ###
          
          

          EDIT: typo in euler code fixed TIG 20110209

          TIG

          1 Reply Last reply Reply Quote 0
          • M Offline
            MartinRinehart
            last edited by 24 Mar 2010, 14:22

            @dburdick said:

            I read your wonderful web-page description of the 4 x 4 matrix - ...

            Very kind words, indeed. Many thanks.

            For those not versed in the Transformation Matrix, my tutorial's Appendix T, introduces it, Appendix MM explains matrix multiplication and Chapter 16 explains how you can live without it (and includes a Matrix class, just in case).

            Author, Edges to Rubies - The Complete SketchUp Tutorial at http://www.MartinRinehart.com/models/tutorial.

            1 Reply Last reply Reply Quote 0
            • D Offline
              Djarlo
              last edited by 8 Feb 2011, 22:50

              @tig said:

              Thanks for the input...
              Here's my revised code using you ideas - it's still in radians and I have tried not to clash with your method naming etc - it now finds x/y/z rotations and [x,y,z] rotation as an array and has extra methods to set rotations from a 3 item array, referring either to the model axes or object's axes...

              ### transformation.extensions.rb (c) TIG 2009
              > ### From original ideas by TBD and others ? ### TIG 20091010
              > ### The euler rotation ideas from Dave Burdick 20100324
              > ### It extends the methods for Geom;;Transformation...
              > ### The built-in method "object.transformation.origin" return a point3d 
              > ###   that is the object's origin/insertion; the new method 
              > ###   object.transformation.getX etc returns the X location of the  
              > ###   object [or Y or Z].
              > ### The setX(x) resets the X value of the object [or Y or Z]; it 
              > ###   returns a new transformation that can then be used to reset the 
              > ###   original transformation; thus;
              > ###   object.transformation=object.transformation.setX(another_object.transformation.getX)
              > ###   - here it makes the object's X = another_object's X ; 
              > ###     it also could be given a float, e.g. 12.345 or a 'variable'...
              > ### The object.transformation.scaleX etc returns the scale on that axis.
              > ### The object.transformation.rotX etc returns the rotation on that axis
              > ###   in radians; use ... .rotX.radians to get it in degrees...
              > ### It uses different names to TBD's, e.g. 'rotZ' instead of 'zrot' etc.
              > ### Note the capitalization... this is because some 'compiled scripts' 
              > ### use 'rotz' already etc - [and they return the rotation in degrees!]
              > ### object.transformation.rotXYZ
              > ###   returns a 3 item array giving the rotations in x/y/z
              > ### object.transformation.rot_a
              > ###   returns an 11 item array of the transformation's rotation/scaling 
              > ###   - it can be used to extract some data more easily or as below...
              > ### object.transformation=object.transformation.rotation_from_rot_a(another_object.transformation.rot_a) 
              > ###   this applies another_object's 'rot_a' to change the object.
              > ### object.transformation=object.transformation.rotation_from(another_object.transformation) 
              > ###   this applies another_object's rotation/scaling to the object.
              > ###   it returns a new transformation that can then be used to reset the original...
              > ### object.transformation=object.transformation.origin_from(another_object.transformation) 
              > ###   this applies another_object's origin/location to the object.
              > ###   it returns a new transformation that can then be used to reset the original...
              > ### object.transformation=object.transformation.rotation_from_xyz([xrot,yrot,zrot])
              > ###   this applies a 3 item array of x/y/z rotations about the model's x/y/z-axes, 
              > ###   this could also be the array returned by rotXYZ.
              > ###   it returns a new transformation that can then be used to reset the original...
              > ### object.transformation=object.transformation.rotation_from_xyz_locally([xrot,yrot,zrot])
              > ###   this applies a 3 item array of x/y/z rotations about the objects's x/y/z-axes, 
              > ###   this could also be the array returned by rotXYZ.
              > ###   it returns a new transformation that can then be used to reset the original...
              > ###
              > class Geom;;Transformation
              >     def euler_angle(xyx=[])
              >        m = self.xaxis.to_a + self.yaxis.to_a + self.zaxis.to_a
              >        if m[6] != 1 and 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 getX
              >     self.to_a[12]
              >   end
              >   def getY
              >     self.to_a[13]
              >   end
              >   def getZ
              >     self.to_a[14]
              >   end
              >   def setX(x)
              >     if not x.class==Float and not x.class==Integer
              >       puts "Transformation;;setX( ) expects a Float or Integer."
              >       return nil
              >     end#if
              >     x=x.to_f
              >     t=self.to_a
              >     t[12]=x
              >     return self.set!(t)
              >   end
              >   def setY(y)
              >     if not y.class==Float and not y.class==Integer
              >       puts "Transformation;;setY( ) expects a Float or Integer."
              >       return nil
              >     end#if
              >     y=y.to_f
              >     t=self.to_a
              >     t[13]=y
              >     return self.set!(t)
              >   end
              >   def setZ(z)
              >     if not z.class==Float and not z.class==Integer
              >       puts "Transformation;;setZ( ) expects a Float or Integer."
              >       return nil
              >     end#if
              >     z=z.to_f
              >     t=self.to_a
              >     t[14]=z
              >     return self.set!(t)
              >   end
              >   def scaleX
              >     Math.sqrt(self.to_a[0]**2+self.to_a[1]**2+self.to_a[2]**2)
              >   end
              >   def scaleY
              >     Math.sqrt(self.to_a[4]**2+self.to_a[5]**2+self.to_a[6]**2)
              >   end
              >   def scaleZ
              >     Math.sqrt(self.to_a[8]**2+self.to_a[9]**2+self.to_a[10]**2)
              >   end
              >   def rotX
              >     #(Math.atan2(self.to_a[9],self.to_a[10]))
              >      #Math.acos(self.to_a[5])
              >      euler_angle(0)
              >   end
              >   def rotY
              >     #(Math.arcsin(self.to_a[8]))
              >     #Math.acos(self.to_a[0])
              >     euler_angle(1)
              >   end
              >   def rotZ
              >     #(-Math.atan2(self.to_a[4],self.to_a[0]))
              >     #Math.asin(self.to_a[4])
              >     euler_angle(2)
              >   end
              >   def rotXYZ
              >     euler_angle
              >   end
              >   def rot_a ### rotation matrix 4x3 3 and 7 are nil
              >     t=self.to_a
              >     r=[]
              >     [0,1,2,3,4,5,6,7,8,9,10].each{|i|r[i]=t[i]}
              >     r[3]=nil
              >     r[7]=nil
              >     return r
              >   end
              >   def rotation_from_xyz(xyz)
              >     if not xyz.class==Array and not xyz[2] and not xyz[0].class==Float and not xyz[1].class==Float and not xyz[2].class==Float
              >       puts "Transformation;;rotation_from_xyz( ) expects a 3 Item Array of Angles [as Floats]."
              >       return nil
              >     end#if
              >     tx=Geom;;Transformation.rotation(self.origin,X_AXIS,xyz[0])
              >     ty=Geom;;Transformation.rotation(self.origin,Y_AXIS,xyz[1])
              >     tz=Geom;;Transformation.rotation(self.origin,Z_AXIS,xyz[2])
              >     t=(tx*ty*tz)
              >     return self.set!(t)
              >   end
              >   def rotation_from_xyz_locally(xyz)
              >     if not xyz.class==Array and not xyz[2] and not xyz[0].class==Float and not xyz[1].class==Float and not xyz[2].class==Float
              >       puts "Transformation;;rotation_from_xyz_locally( ) expects a 3 Item Array of Angles [as Floats]."
              >       return nil
              >     end#if
              >     tx=Geom;;Transformation.rotation(self.origin,self.xaxis,xyz[0])
              >     ty=Geom;;Transformation.rotation(self.origin,self.yaxis,xyz[1])
              >     tz=Geom;;Transformation.rotation(self.origin,self.zaxis,xyz[2])
              >     t=(tx*ty*tz)
              >     return self.set!(t)
              >   end
              >   def rotation_from_rot_a(rot_a)
              >     if not rot_a.class==Array and not rot_a[10]
              >       puts "Transformation;;rotation_from_rot_a( ) expects an 11 Item Array."
              >       return nil
              >     end#if
              >     t=self.to_a
              >     [0,1,2,4,5,6,8,9,10].each{|i|t[i]=rot_a[i].to_f}
              >     return self.set!(t)
              >   end
              >   def rotation_from(trans)
              >     if not trans.class==Geom;;Transformation
              >       puts "Transformation;;rotation_from( ) expects a Sketchup;;Transformation."
              >       return nil
              >     end#if
              >     t=self.to_a
              >     tt=trans.to_a
              >     [0,1,2,4,5,6,8,9,10].each{|i|t[i]=tt[i].to_f}
              >     return self.set!(t)
              >   end
              >   def origin_from(trans)
              >     if not trans.class==Geom;;Transformation
              >       puts "Transformation;;origin_from( ) expects a Sketchup;;Transformation."
              >       return nil
              >     end#if
              >     t=self.to_a
              >     tt=trans.to_a
              >     [12,13,14].each{|i|t[i]=tt[i].to_f}
              >     return self.set!(t)
              >   end
              > end#class
              > ###
              > 
              

              Hey Tig, thanks for the great code, i might be doing something wrong here but this:

              	Selection = Sketchup.active_model.selection[0]
              	Rotation = Selection.transformation.rotXYZ
              

              Gives an error,

              Error; #<NameError; undefined local variable or method `xyz' for #<Geom;;Transformation;0xa747534>>
              D;/PROGRA~1/Google/GOOGLE~1/Plugins/test.rb;128;in `euler_angle'
              D;/PROGRA~1/Google/GOOGLE~1/Plugins/test.rb;197;in `rotXYZ'
              D;/PROGRA~1/Google/GOOGLE~1/Plugins/test.rb;64
              

              Any suggestions what could be going wrong?

              1 Reply Last reply Reply Quote 0
              • T Offline
                TIG Moderator
                last edited by 9 Feb 2011, 11:20

                @djarlo said:

                @tig said:

                Thanks for the input...
                Here's my revised code using you ideas - it's still in radians and I have tried not to clash with your method naming etc - it now finds x/y/z rotations and [x,y,z] rotation as an array and has extra methods to set rotations from a 3 item array, referring either to the model axes or object's axes...

                ### transformation.extensions.rb (c) TIG 2009
                > > ### From original ideas by TBD and others ? ### TIG 20091010
                > > ### The euler rotation ideas from Dave Burdick 20100324
                > > ### It extends the methods for Geom;;Transformation...
                > > ### The built-in method "object.transformation.origin" return a point3d 
                > > ###   that is the object's origin/insertion; the new method 
                > > ###   object.transformation.getX etc returns the X location of the  
                > > ###   object [or Y or Z].
                > > ### The setX(x) resets the X value of the object [or Y or Z]; it 
                > > ###   returns a new transformation that can then be used to reset the 
                > > ###   original transformation; thus;
                > > ###   object.transformation=object.transformation.setX(another_object.transformation.getX)
                > > ###   - here it makes the object's X = another_object's X ; 
                > > ###     it also could be given a float, e.g. 12.345 or a 'variable'...
                > > ### The object.transformation.scaleX etc returns the scale on that axis.
                > > ### The object.transformation.rotX etc returns the rotation on that axis
                > > ###   in radians; use ... .rotX.radians to get it in degrees...
                > > ### It uses different names to TBD's, e.g. 'rotZ' instead of 'zrot' etc.
                > > ### Note the capitalization... this is because some 'compiled scripts' 
                > > ### use 'rotz' already etc - [and they return the rotation in degrees!]
                > > ### object.transformation.rotXYZ
                > > ###   returns a 3 item array giving the rotations in x/y/z
                > > ### object.transformation.rot_a
                > > ###   returns an 11 item array of the transformation's rotation/scaling 
                > > ###   - it can be used to extract some data more easily or as below...
                > > ### object.transformation=object.transformation.rotation_from_rot_a(another_object.transformation.rot_a) 
                > > ###   this applies another_object's 'rot_a' to change the object.
                > > ### object.transformation=object.transformation.rotation_from(another_object.transformation) 
                > > ###   this applies another_object's rotation/scaling to the object.
                > > ###   it returns a new transformation that can then be used to reset the original...
                > > ### object.transformation=object.transformation.origin_from(another_object.transformation) 
                > > ###   this applies another_object's origin/location to the object.
                > > ###   it returns a new transformation that can then be used to reset the original...
                > > ### object.transformation=object.transformation.rotation_from_xyz([xrot,yrot,zrot])
                > > ###   this applies a 3 item array of x/y/z rotations about the model's x/y/z-axes, 
                > > ###   this could also be the array returned by rotXYZ.
                > > ###   it returns a new transformation that can then be used to reset the original...
                > > ### object.transformation=object.transformation.rotation_from_xyz_locally([xrot,yrot,zrot])
                > > ###   this applies a 3 item array of x/y/z rotations about the objects's x/y/z-axes, 
                > > ###   this could also be the array returned by rotXYZ.
                > > ###   it returns a new transformation that can then be used to reset the original...
                > > ###
                > > class Geom;;Transformation
                > >     def euler_angle(xyx=[])
                > >        m = self.xaxis.to_a + self.yaxis.to_a + self.zaxis.to_a
                > >        if m[6] != 1 and 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 getX
                > >     self.to_a[12]
                > >   end
                > >   def getY
                > >     self.to_a[13]
                > >   end
                > >   def getZ
                > >     self.to_a[14]
                > >   end
                > >   def setX(x)
                > >     if not x.class==Float and not x.class==Integer
                > >       puts "Transformation;;setX( ) expects a Float or Integer."
                > >       return nil
                > >     end#if
                > >     x=x.to_f
                > >     t=self.to_a
                > >     t[12]=x
                > >     return self.set!(t)
                > >   end
                > >   def setY(y)
                > >     if not y.class==Float and not y.class==Integer
                > >       puts "Transformation;;setY( ) expects a Float or Integer."
                > >       return nil
                > >     end#if
                > >     y=y.to_f
                > >     t=self.to_a
                > >     t[13]=y
                > >     return self.set!(t)
                > >   end
                > >   def setZ(z)
                > >     if not z.class==Float and not z.class==Integer
                > >       puts "Transformation;;setZ( ) expects a Float or Integer."
                > >       return nil
                > >     end#if
                > >     z=z.to_f
                > >     t=self.to_a
                > >     t[14]=z
                > >     return self.set!(t)
                > >   end
                > >   def scaleX
                > >     Math.sqrt(self.to_a[0]**2+self.to_a[1]**2+self.to_a[2]**2)
                > >   end
                > >   def scaleY
                > >     Math.sqrt(self.to_a[4]**2+self.to_a[5]**2+self.to_a[6]**2)
                > >   end
                > >   def scaleZ
                > >     Math.sqrt(self.to_a[8]**2+self.to_a[9]**2+self.to_a[10]**2)
                > >   end
                > >   def rotX
                > >     #(Math.atan2(self.to_a[9],self.to_a[10]))
                > >      #Math.acos(self.to_a[5])
                > >      euler_angle(0)
                > >   end
                > >   def rotY
                > >     #(Math.arcsin(self.to_a[8]))
                > >     #Math.acos(self.to_a[0])
                > >     euler_angle(1)
                > >   end
                > >   def rotZ
                > >     #(-Math.atan2(self.to_a[4],self.to_a[0]))
                > >     #Math.asin(self.to_a[4])
                > >     euler_angle(2)
                > >   end
                > >   def rotXYZ
                > >     euler_angle
                > >   end
                > >   def rot_a ### rotation matrix 4x3 3 and 7 are nil
                > >     t=self.to_a
                > >     r=[]
                > >     [0,1,2,3,4,5,6,7,8,9,10].each{|i|r[i]=t[i]}
                > >     r[3]=nil
                > >     r[7]=nil
                > >     return r
                > >   end
                > >   def rotation_from_xyz(xyz)
                > >     if not xyz.class==Array and not xyz[2] and not xyz[0].class==Float and not xyz[1].class==Float and not xyz[2].class==Float
                > >       puts "Transformation;;rotation_from_xyz( ) expects a 3 Item Array of Angles [as Floats]."
                > >       return nil
                > >     end#if
                > >     tx=Geom;;Transformation.rotation(self.origin,X_AXIS,xyz[0])
                > >     ty=Geom;;Transformation.rotation(self.origin,Y_AXIS,xyz[1])
                > >     tz=Geom;;Transformation.rotation(self.origin,Z_AXIS,xyz[2])
                > >     t=(tx*ty*tz)
                > >     return self.set!(t)
                > >   end
                > >   def rotation_from_xyz_locally(xyz)
                > >     if not xyz.class==Array and not xyz[2] and not xyz[0].class==Float and not xyz[1].class==Float and not xyz[2].class==Float
                > >       puts "Transformation;;rotation_from_xyz_locally( ) expects a 3 Item Array of Angles [as Floats]."
                > >       return nil
                > >     end#if
                > >     tx=Geom;;Transformation.rotation(self.origin,self.xaxis,xyz[0])
                > >     ty=Geom;;Transformation.rotation(self.origin,self.yaxis,xyz[1])
                > >     tz=Geom;;Transformation.rotation(self.origin,self.zaxis,xyz[2])
                > >     t=(tx*ty*tz)
                > >     return self.set!(t)
                > >   end
                > >   def rotation_from_rot_a(rot_a)
                > >     if not rot_a.class==Array and not rot_a[10]
                > >       puts "Transformation;;rotation_from_rot_a( ) expects an 11 Item Array."
                > >       return nil
                > >     end#if
                > >     t=self.to_a
                > >     [0,1,2,4,5,6,8,9,10].each{|i|t[i]=rot_a[i].to_f}
                > >     return self.set!(t)
                > >   end
                > >   def rotation_from(trans)
                > >     if not trans.class==Geom;;Transformation
                > >       puts "Transformation;;rotation_from( ) expects a Sketchup;;Transformation."
                > >       return nil
                > >     end#if
                > >     t=self.to_a
                > >     tt=trans.to_a
                > >     [0,1,2,4,5,6,8,9,10].each{|i|t[i]=tt[i].to_f}
                > >     return self.set!(t)
                > >   end
                > >   def origin_from(trans)
                > >     if not trans.class==Geom;;Transformation
                > >       puts "Transformation;;origin_from( ) expects a Sketchup;;Transformation."
                > >       return nil
                > >     end#if
                > >     t=self.to_a
                > >     tt=trans.to_a
                > >     [12,13,14].each{|i|t[i]=tt[i].to_f}
                > >     return self.set!(t)
                > >   end
                > > end#class
                > > ###
                > > 
                

                Hey Tig, thanks for the great code, i might be doing something wrong here but this:

                	Selection = Sketchup.active_model.selection[0]
                > 	Rotation = Selection.transformation.rotXYZ
                

                Gives an error,

                Error; #<NameError; undefined local variable or method `xyz' for #<Geom;;Transformation;0xa747534>>
                > D;/PROGRA~1/Google/GOOGLE~1/Plugins/test.rb;128;in `euler_angle'
                > D;/PROGRA~1/Google/GOOGLE~1/Plugins/test.rb;197;in `rotXYZ'
                > D;/PROGRA~1/Google/GOOGLE~1/Plugins/test.rb;64
                

                Any suggestions what could be going wrong?

                ๐Ÿ˜ณ There's a typo in the code that I corrected but never posted !
                def euler_angle(xyx=[])
                should be
                def euler_angle(xyz=[])
                also note what Dan said about naming conventions.....

                Here's a link to the corrected code http://forums.sketchucation.com/viewtopic.php?p=190874#p190874

                TIG

                1 Reply Last reply Reply Quote 0
                • S Offline
                  shirazbj
                  last edited by 28 Feb 2011, 11:37

                  @martinrinehart said:

                  @dburdick said:

                  I read your wonderful web-page description of the 4 x 4 matrix - ...

                  I am reading it too. Great job.Thanks.

                  1 Reply Last reply Reply Quote 0
                  • D Offline
                    Djarlo
                    last edited by 28 Feb 2011, 13:52

                    Thanks to both ๐Ÿ˜„
                    heh must have missed that type too time and again i looked it all over to see whats wrong ๐Ÿ˜›

                    1 Reply Last reply Reply Quote 0
                    • T Offline
                      tomasz
                      last edited by 28 Sept 2011, 10:40

                      @martinrinehart said:

                      @dburdick said:

                      I read your wonderful web-page description of the 4 x 4 matrix - ...

                      Very kind words, indeed. Many thanks.

                      For those not versed in the Transformation Matrix, my tutorial's Appendix T, introduces it, Appendix MM explains matrix multiplication and Chapter 16 explains how you can live without it (and includes a Matrix class, just in case).

                      Thank you Martin for those wonderful tutorials. I wasn't aware that transformation.to_a[15] is a divisor for a translation! As far as I am aware SketchUp itself keeps it equal to 1, but some plugins modify Wt (i.e. Component Stringer).

                      Author of [Thea Render for SketchUp](http://www.thearender.com/sketchup)

                      1 Reply Last reply Reply Quote 0
                      • R Offline
                        Ruts
                        last edited by 27 Aug 2015, 09:19

                        I have a question about the euler_angle method, especially about the xyz. I have searched the web and found information about this case, but it didn't result in an answer.

                        Here's how I read this piece of code:

                        when you call the method euler_angles you create an empty array under variable xyz as parameter. Then nothing happens with the array for the whole piece of code. At the end the array can be ==0 (contains one element which is equal to zero), ==1, ==2 or still empty. How can it be that the array can contain elements?

                        1 Reply Last reply Reply Quote 0
                        • Dan RathbunD Offline
                          Dan Rathbun
                          last edited by 27 Aug 2015, 17:29

                          Words in ALLCAPS are reserved for constants.
                          Words in Titlecase are for Class and Module Identifiers, (which are also constants. Any word the starts with a capital character is a constant.)

                          Bad:
                          Selection = Sketchup.active_model.selection[0] Rotation = Selection.transformation.rotXYZ

                          Good for variables:
                          selection = Sketchup.active_model.selection[0] rotation = selection.transformation.rotXYZ
                          ๐Ÿ˜‰

                          I'm not here much anymore.

                          1 Reply Last reply Reply Quote 0
                          • Dan RathbunD Offline
                            Dan Rathbun
                            last edited by 27 Aug 2015, 17:56

                            @ruts said:

                            ...when you call the method euler_angle you create an empty array under variable xyz as parameter.

                            The secret is that Ruby does not really "have" variables, even though the books use that name. Ruby has references that point at objects, and a reference can be made to point at any class of object, and then later be re-assigned to point at any other object of any class, at any time.
                            This is referred to as "weakly typed", but really Ruby references are not type locked at all.

                            @ruts said:

                            Then nothing happens with the array for the whole piece of code.

                            Because it (the argument) is just being used as a switch to tell the method what the coder wants as an output. An empty array object is simply the default, which tells the method the calling code expects an array as a return object.

                            @ruts said:

                            At the end the array can be ==0 (contains one element which is equal to zero), ==1, ==2 or still empty. How can it be that the array can contain elements?

                            It cannot. You mis-understand. The method is not testing an array, it is testing a reference to the method argument to see if is pointing at integers 0 or 1 or 2, or still pointing at [] (the default empty array object,) and then returning either the indicated values, or an array of all three.

                            IF you call the method with no arguments, or like euler_angle([]) you will get an array of 3 values.
                            IF you call the method thus: euler_angle(0) you will get the x value returned.
                            IF you call the method thus: euler_angle(1) you will get the y value returned.
                            IF you call the method thus: euler_angle(2) you will get the z value returned.

                            The 0, 1 and 2 subscripts come from the SketchUp API's extension of the Array class, where 3 element arrays can act like points and vectors.

                            Still have not read the book, I see.

                            I'm not here much anymore.

                            1 Reply Last reply Reply Quote 0
                            • R Offline
                              Ruts
                              last edited by 17 Sept 2015, 11:42

                              I did delete the rotX/Y/Z methods, that's why I didn't understand where the variable xyz came from and why it could get value = 0/1/2. I do understand how methods with there variables work. Sorry was confused.

                              On the other hand, I have been working with the euler_angle method for a while and now that I'm closely getting to a complete script I have noticed this method only partial works (for me?). Does it work for you guys? When I did draw shapes with a given rotation, the method didn't succeed to calculate the right angles for me so I did some research and made some changes. Here's the code I use:

                              	def self.rotation(trans)
                              		b = [trans.xaxis.to_a, trans.yaxis.to_a, trans.zaxis.to_a]
                              		m = b.transpose.flatten!
                              		if m[6] != 1 and 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
                              			phipos = Math.atan2(m[1],m[2])
                              			phineg = Math.atan2(-m[1],-m[2])
                              			if m[6] == -1
                              				ry = Math;;PI/2
                              				rx = rz + phipos
                              			else
                              				ry = -Math;;PI/2
                              				rx = -rz + phineg
                              			end
                              		end   
                              	return [rx.radians,ry.radians,rz.radians]
                              	end	
                              

                              First I found this document (page 5) that explains the calculations the way you do it. But it seems that you miss little pieces of code to make it complete.

                              When I did calculate the angles it yet didn't calculate the right angles. So I did try some things and found out that the angles that were calculated represent the transpose matrix of the rotation that I need. So I fixed this by transposing the rotation matrix before the calculations.

                              With these changes this piece of code does calculate the right angles. Did your code work for you?

                              1 Reply Last reply Reply Quote 0
                              • Dan RathbunD Offline
                                Dan Rathbun
                                last edited by 17 Sept 2015, 16:50

                                You realize that this topic thread is like 4 and 1/2 years old ?

                                I'm not here much anymore.

                                1 Reply Last reply Reply Quote 0
                                • R Offline
                                  Ruts
                                  last edited by 17 Sept 2015, 18:12

                                  @dan rathbun said:

                                  You realize that this topic thread is like 4 and 1/2 years old ?

                                  Yes, I have seen it. That does not mean that people can't come here and look for code that works? That's how I landed here. I was looking for some code that could convert the rotation matrix to euler angles. I found out that this code didn't work for me, so I did correct it and now I share it here just in case people experience the same problem as me. I'm just excited that I finally can contribute a little instead of always asking things!

                                  Nothing wrong with that, right?

                                  1 Reply Last reply Reply Quote 0
                                  • Dan RathbunD Offline
                                    Dan Rathbun
                                    last edited by 21 Sept 2015, 20:05

                                    @ruts said:

                                    @dan rathbun said:

                                    You realize that this topic thread is like 4 and 1/2 years old ?

                                    Yes, I have seen it. ... Nothing wrong with that, right?

                                    I was referring to your asking questions of original posters, who might not remember, or might not even have been active here for some time.

                                    I'm not here much anymore.

                                    1 Reply Last reply Reply Quote 0
                                    • J Offline
                                      Jernej Vidmar
                                      last edited by 4 Jun 2016, 09:32

                                      Hello transformation gurus!

                                      just to double check the euler_angle(xyz=[]) method ... is the condition:

                                      if m[6] != 1 and m[6]!= 1
                                      

                                      at the beginning correct? Why double checking the same variable against the same value? Or is it meant to be:

                                      if m[6] != 1 and m[6]!= -1
                                      

                                      ?

                                      Cheers,
                                      Jernej

                                      1 Reply Last reply Reply Quote 0
                                      • T Offline
                                        TIG Moderator
                                        last edited by 4 Jun 2016, 13:22

                                        An olde typo - use 1 for the second test...

                                        TIG

                                        1 Reply Last reply Reply Quote 0
                                        • 1
                                        • 2
                                        • 1 / 2
                                        • First post
                                          Last post
                                        Buy SketchPlus
                                        Buy SUbD
                                        Buy WrapR
                                        Buy eBook
                                        Buy Modelur
                                        Buy Vertex Tools
                                        Buy SketchCuisine
                                        Buy FormFonts

                                        Advertisement