INTERNAL: Getting rotation angles about the [xyz]-axis from the transformation matrix
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