• 登入
sketchucation logo sketchucation
  • 登入
🤑 SketchPlus 1.3 | 44 Tools for $15 until June 20th Buy Now

Compute Rotation and Scale from Transform Object

已排程 已置頂 已鎖定 已移動 Developers' Forum
26 貼文 9 Posters 4.6k 瀏覽
正在載入更多貼文
  • 從舊到新
  • 從新到舊
  • 最多點贊
回覆
  • 在新貼文中回覆
登入後回覆
此主題已被刪除。只有擁有主題管理權限的使用者可以查看。
  • D 離線
    dburdick
    最後由 編輯 2010年3月23日 上午11:22

    Is there a simple Sketchup Ruby method which returns the x,y and z rotation in degrees about the world axis and the x,z, and z scale given a component transform object? I can't seem to find anything in the API for this and I would prefer to not have to dust off my old trig book.

    1 條回覆 最後回覆 回覆 引用 0
    • T 離線
      TIG Moderator
      最後由 編輯 2010年3月23日 上午11:43

      Copy all of this and paste it into a plain text file in ../Plugins/ called something like transformation_extensions.rb

      ### transformation.extensions.rb (c) TIG 2009
      ### From original ideas by TBD and others ? ### TIG 20091010
      ### 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.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 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 another_object's origin/location to the object.
      ###   it returns a new transformation that can then be used to reset the original...
      ###
      class Geom;;Transformation
        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.clone.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.clone.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.clone.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])
        end
        def rotY
          #(Math.arcsin(self.to_a[8]))
          Math.acos(self.to_a[0])
        end
        def rotZ
          #(-Math.atan2(self.to_a[4],self.to_a[0]))
          Math.asin(self.to_a[4])
        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_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 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.clone.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.clone.set!(t)
        end
      end#class
      ###
      
      

      These methods return/set lots of different bits of transformation info...
      The rotX/rotY/rotZ are what you want for 'rotations'. They report in 'radians' but it's easily translated...
      So for example to find the rotation in degrees of an 'object' about the main Z axis use z_rotation=object.transformation.rotZ.radians where the .radians method returns it as a number in degrees...
      Also use scaleX/scaleY/scaleZ for the 'scale' - where z_scale=object.transformation.scaleZ returns the scale factor in the Z...
      🤓

      TIG

      1 條回覆 最後回覆 回覆 引用 0
      • D 離線
        dburdick
        最後由 編輯 2010年3月23日 上午11:58

        Wow, thanks TIG. That looks like a tremendously useful set of extensions you've cooked up there. I'll put them to good use.

        1 條回覆 最後回覆 回覆 引用 0
        • D 離線
          dburdick
          最後由 編輯 2010年3月23日 下午1:39

          Hi Tig,

          I tried your transformation extension routines and everything works fine if the component is scaled uniformly. However, when you scale the component non-uniformly, (e.g. scale out in only the X direction), the rotation values return incoreectly and/or the program bombs because the acos function you use is out of range. Is there someway to appy a scaling to your extension rotation values beofre computing the acos.

          1 條回覆 最後回覆 回覆 引用 0
          • M 離線
            MartinRinehart
            最後由 編輯 2010年3月23日 下午2:25

            @dburdick said:

            Is there a simple Sketchup Ruby method which returns the x,y and z rotation in degrees ...

            What's the application for this? (Scaling modifies the xform's [0,0], [1,1], [2,2] entries. Rotations modify all entries from [0,0] through [2,2]. It's not immediately obvious that you can find original operations in any non-trivial case.)

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

            1 條回覆 最後回覆 回覆 引用 0
            • D 離線
              dburdick
              最後由 編輯 2010年3月23日 下午9:59

              @martinrinehart said:

              @dburdick said:

              Is there a simple Sketchup Ruby method which returns the x,y and z rotation in degrees ...

              What's the application for this? (Scaling modifies the xform's [0,0], [1,1], [2,2] entries. Rotations modify all entries from [0,0] through [2,2]. It's not immediately obvious that you can find original operations in any non-trivial case.)

              Hi Martin,

              Thanks for jumping in on this. I read your wonderful web-page description of the 4 x 4 matrix - truly excellent - and serves as the inspiration behind what I'm trying to do.

              Basically, I need to get the exact rotation and scaling values of any component in world space in order to export an instance of that component. I already have figured out how to create the transformation by multiplying the stacked transformations of the component hierarchy, but I need to express the output rotational transformation in degrees - eg rotX, rotY, rotZ - and it needs to work even if the user decides to scale the component non-uniformly (e.g. skewing, shearing). In Sketchup, a user could for example scale a door component in only the Y direction. The current transform extension lib from TIG does not handle this correctly as the lib assumes all scaling is done uniformly to the component. So I need to understand how to adjust TIG's ruby code to accomodate this. Here's his code for example on returing the rotX value from a transform object:

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

              This works fine when the component is uniformly scaled, but fails when for example the x dimension is scaled to a value different than Y and Z. This is because the array value[5] contains a number greater than 1.0 when non-uniform scaling is introduced. So it will return an error and bomb. I;m not sure what the commented out code is above the equation.

              1 條回覆 最後回覆 回覆 引用 0
              • T 離線
                TIG Moderator
                最後由 編輯 2010年3月23日 下午10:06

                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...

                TIG

                1 條回覆 最後回覆 回覆 引用 0
                • D 離線
                  dburdick
                  最後由 編輯 2010年3月24日 上午2: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 條回覆 最後回覆 回覆 引用 0
                  • D 離線
                    dburdick
                    最後由 編輯 2010年3月24日 上午3: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 條回覆 最後回覆 回覆 引用 0
                    • D 離線
                      dburdick
                      最後由 編輯 2010年3月24日 上午6: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 條回覆 最後回覆 回覆 引用 0
                      • T 離線
                        TIG Moderator
                        最後由 編輯 2010年3月24日 上午9: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 條回覆 最後回覆 回覆 引用 0
                        • M 離線
                          MartinRinehart
                          最後由 編輯 2010年3月24日 下午2: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 條回覆 最後回覆 回覆 引用 0
                          • D 離線
                            Djarlo
                            最後由 編輯 2011年2月8日 下午10: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 條回覆 最後回覆 回覆 引用 0
                            • T 離線
                              TIG Moderator
                              最後由 編輯 2011年2月9日 上午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 條回覆 最後回覆 回覆 引用 0
                              • S 離線
                                shirazbj
                                最後由 編輯 2011年2月28日 上午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 條回覆 最後回覆 回覆 引用 0
                                • D 離線
                                  Djarlo
                                  最後由 編輯 2011年2月28日 下午1:52

                                  Thanks to both 😄
                                  heh must have missed that type too time and again i looked it all over to see whats wrong 😛

                                  1 條回覆 最後回覆 回覆 引用 0
                                  • T 離線
                                    tomasz
                                    最後由 編輯 2011年9月28日 上午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 條回覆 最後回覆 回覆 引用 0
                                    • R 離線
                                      Ruts
                                      最後由 編輯 2015年8月27日 上午9: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 條回覆 最後回覆 回覆 引用 0
                                      • Dan RathbunD 離線
                                        Dan Rathbun
                                        最後由 編輯 2015年8月27日 下午5: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 條回覆 最後回覆 回覆 引用 0
                                        • Dan RathbunD 離線
                                          Dan Rathbun
                                          最後由 編輯 2015年8月27日 下午5: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 條回覆 最後回覆 回覆 引用 0
                                          • 1
                                          • 2
                                          • 1 / 2
                                          • 第一個貼文
                                            最後的貼文
                                          Buy SketchPlus
                                          Buy SUbD
                                          Buy WrapR
                                          Buy eBook
                                          Buy Modelur
                                          Buy Vertex Tools
                                          Buy SketchCuisine
                                          Buy FormFonts

                                          Advertisement