X
29 Rate this article:
No rating

INTERNAL: Getting rotation angles about the [xyz]-axis from the transformation matrix

Zachary Norman
Topic:
This tech tip provides an example that demonstrates how the rotation angle about an axis can be calculated using the transformation matrix.Discussion:
The following routine will return the rotation about the x-axis, y-axis and z-axis for a given transformation matrix:

;---------------------------------------------------------------
function get_rotation_angles, tm

rotArray = fltarr(3)

rotArray[1] = asin(tm[2,0])
c = cos(rotArray[1])

if abs(c) lt 1e-5 then begin

rotArray[0] = 0
rotArray[2] = atan(tm[0,1],tm[1,1])

endif else begin

rotArray[0] = atan(-tm[2,1]/c,tm[2,2]/c)
rotArray[2] = atan(-tm[1,0]/c,tm[0,0]/c)

endelse

return, rotArray

end
;---------------------------------------------------------------


Here is an example routine that displays these rotation angles for a model that can be manipulated via the trackball:Solution:

;+
;
; This function calculates the rotation about the
; x-axis, y-axis and z-axis for a given transformation
; matrix
;
;-
function get_rotation_angles, tm
    
    rotArray = fltarr(3)
    
    rotArray[1] = asin(tm[2,0])
    c = cos(rotArray[1])

    if abs(c) lt 1e-5 then begin
    
        rotArray[0] = 0
        rotArray[2] = atan(tm[0,1],tm[1,1])

    endif else begin
    
        rotArray[0] = atan(-tm[2,1]/c,tm[2,2]/c)
        rotArray[2] = atan(-tm[1,0]/c,tm[0,0]/c)
        
    endelse

    return, rotArray

end


;+
;
; Updates the output label text
;
;-
pro update_labels, labels, values, $
    CONVERSION = c

    if n_elements(values) ne 3 then begin
    
        tempVar = 0.0
        values = fltarr(3)
        for i = 0, 2 do begin
        
            widget_control, labels[i], GET_VALUE = strVal
            reads, strVal, tempVar
            values[i] = tempVar

        endfor

    endif

    for i = 0, 2 do $
        widget_control, labels[i], SET_VALUE = $
            string(values[i]*c, FORMAT = '(f8.3)')

end


;+
;
; Free heap variables
;
;-
pro clean_event, tlb

    widget_control, tlb, GET_UVALUE = pState
    obj_destroy, [(*pState).oTrack,(*pState).oWindow]
    ptr_free, pState

end


;+
;
; Handles events from the draw widget
;
;-
pro draw_event, event

    widget_control, event.top, GET_UVALUE = pState

    if event.type eq 4 then begin
    
        (*pState).oWindow -> Draw
        return

    endif

    if (*pState).oTrack -> Update(event, TRANSFORM = tm) then begin
    
        (*pState).oModel -> getProperty, TRANSFORM = tmOld
        tmNew = tmOld#tm
        
        update_labels, (*pState).rotLabels, get_rotation_angles(tmNew), $
            CONVERSION = (*pState).convConst

        (*pState).oModel -> setProperty, TRANSFORM = tmNew
        (*pState).oWindow -> Draw

    endif


end


;+
;
; All other events
;
;-
pro transform_rotation_example_event, event

    widget_control, event.top, GET_UVALUE = pState
    widget_control, event.id, GET_UVALUE = uval
    
    case uval of
    
        'all': (*pState).oTrack -> Reset, (*pState).tCenter, (*pState).tRadius
        
        'deg': begin
        
            if event.select then begin
                (*pState).convConst = !radeg
                update_labels, (*pState).rotLabels, CONVERSION = (*pState).convConst
            endif
            
        end        

        'rad': begin
        
            if event.select then begin
                (*pState).convConst = 1.0
                update_labels, (*pState).rotLabels, CONVERSION = !dtor
            endif            

         end

        'reset': begin
        
            (*pState).oModel -> Reset
            (*pState).oWindow -> Draw
            update_labels, (*pState).rotLabels, replicate(0.0,3), $
                CONVERSION = 1.0

        end

        'x': (*pState).oTrack -> Reset, (*pState).tCenter, (*pState).tRadius, $
            AXIS = 0, /CONSTRAIN
            
        'y': (*pState).oTrack -> Reset, (*pState).tCenter, (*pState).tRadius, $
            AXIS = 1, /CONSTRAIN
            
        'z': (*pState).oTrack -> Reset, (*pState).tCenter, (*pState).tRadius, $
            AXIS = 2, /CONSTRAIN
            
        else:

    endcase

end


;+
;
; Main routine
;
;-
pro transform_rotation_example

    ss = get_screen_size()
    drawDims = [500,500]

    tlb = widget_base(COL = 1, MAP = 0)
    draw = widget_draw(tlb, GRAPHICS_LEVEL = 2, $
        XSIZE = drawDims[0], YSIZE = drawDims[1], $
        /MOTION_EVENTS, /BUTTON_EVENTS, /EXPOSE_EVENTS, $
        EVENT_PRO = 'draw_event')

    base = widget_base(tlb, row = 1)
    
    axisBase = widget_base(base, COL = 1, /FRAME)
    void = widget_label(axisBase, VALUE = 'Rotation Axis')
    exBase = widget_base(axisBase, /EXCLUSIVE, COL = 1)
    void = widget_button(exBase, VALUE = 'X-Axis', UVALUE = 'x')
    void = widget_button(exBase, VALUE = 'Y-Axis', UVALUE = 'y')
    void = widget_button(exBase, VALUE = 'Z-Axis', UVALUE = 'z')
    void = widget_button(exBase, VALUE = 'All', UVALUE = 'all')
    widget_control, void, /SET_BUTTON

    buttonBase = widget_base(base, COL = 1, /FRAME)
    label = widget_label(buttonBase, VALUE = 'Display')
    exBase = widget_base(buttonBase, /EXCLUSIVE)
    void = widget_button(exBase, VALUE = 'Degrees', UVALUE = 'deg')
    void = widget_button(exBase, VALUE = 'Radians', UVALUE = 'rad')
    widget_control, void, /SET_BUTTON
    void = widget_button(buttonBase, VALUE = 'Reset', UVALUE = 'reset') 

    labelBase = widget_base(base, COL = 1, /FRAME)
    void = widget_label(labelBase, VALUE = 'Rotation About...')
    labelBase = widget_base(labelBase, COL = 2)
    void = widget_label(labelBase, VALUE = 'X-Axis : ')
    void = widget_label(labelBase, VALUE = 'Y-Axis : ')
    void = widget_label(labelBase, VALUE = 'Z-Axis : ')
    rotLabels = lonarr(3)
    rotLabels[0] = widget_label(labelBase, VALUE = '     0.000')
    rotLabels[1] = widget_label(labelBase, VALUE = '     0.000')
    rotLabels[2] = widget_label(labelBase, VALUE = '     0.000')

    widget_control, tlb, /REALIZE

    widget_control, draw, GET_VALUE = oWindow
    oSurface = obj_new('IDLgrSurface', hanning(30,30), $
        COLOR = [0,255,0], STYLE = 2)
        
    oSurface -> getProperty, XRANGE = xr, $
                             YRANGE = yr, $
                             ZRANGE = zr
    xc=norm_coord(xr) & yc=norm_coord(yr) & zc=norm_coord(zr)
    xc[0]=xc[0]-0.5 & yc[0]=yc[0]-0.5 & zc[0]=zc[0]-0.5
    oSurface -> setProperty, XCOORD_CONV = xc, $
                             YCOORD_CONV = yc, $
                             ZCOORD_CONV = zc

    oLight = obj_new('IDLgrLight', LOCATION = [-1,1,1], TYPE = 1)
    oModel = obj_new('IDLgrModel')
    oView = obj_new('IDLgrView', COLOR = replicate(255B,3))
    
    oModel -> Add, [oSurface,oLight]
    oView -> Add, oModel
    oWindow -> SetProperty, GRAPHICS_TREE = oView

    tCenter = drawDims/2 & tRadius = max(drawDims)/2
    oTrack = obj_new('TrackBall', tCenter, tRadius)

    state = {oModel:oModel, $
             oWindow:oWindow, $
             oTrack:oTrack, $
             tCenter:tCenter, $
             tRadius:tRadius, $
             rotLabels:rotLabels, $
             convConst:1.0}

    tlbGeom = widget_info(tlb, /GEOM)
    widget_control, tlb, XOFF = (ss[0]-tlbGeom.scr_xsize)/2, $
                         YOFF = (ss[1]-tlbGeom.scr_ysize)/2, $
                         SET_UVALUE = ptr_new(state), MAP = 1
    oWindow -> Draw

    xmanager, 'transform_rotation_example', tlb, CLEANUP = 'clean_event'

end