Rheading = [ cos(h) -sin(h) 0 ]
[ sin(h) cos(h) 0 ]
[ 0 0 1 ]then Rheading * (x,y,z) results in a vector that has been rotated about the Z axis (heading if you allow Z to be vertical). for example Rh*(1,0,0) -> (cos(h), -sin(h), 0).2 more matrices can be written for rotation about Y and X, multiplying the matrices gives a total rotation matrix for the Euler angles.A simple package to perform symbolic manipulation of Euler matrices (and others)
# PROCS
# rotmat {axis angle} - create a 3*3 array of elements for rotation about xyz
# identitymat - returns an identity matrix.
# matmultiply multiply symbols, returns 3*3 symbolic matrix.
# matprint - prints symbolic matrix
# getmatvalue m args = substitute values for parameters and return list of rows in matrix
#
proc identitymat {} { ;# return identity matrix
variable m
foreach i {1 2 3} {
foreach j {1 2 3} {
set m($i,$j) [expr {$i == $j}]
}
}
return [array get m]
}
proc rotmat {axis angle} {
# make a 3 by 3 rotation matrix for angle about axis (x,y or z)
# angle can be a number or a variable name.
# Returned matrix is symbolic NOT numeric.
# Use "getmatvalue" to get numerical values for the matrix.
# eg array set m [rotmat Z p] - returns m(1,1) = m(2,2)=cos(p) etc
variable m
array set m [identitymat]
switch -nocase -- $axis {
x {
array set m [list {2,2} cos($angle) {3,3} cos($angle) \
{2,3} sin($angle) {3,2} -sin($angle)]
} y {
array set m [list {1,1} cos($angle) {3,3} cos($angle) \
{1,3} -sin($angle) {3,1} sin($angle)]
} z {
array set m [list {1,1} cos($angle) {2,2} cos($angle) \
{1,2} sin($angle) {2,1} -sin($angle)]
}}
return [array get m]
}
proc matmultiply {left right} {
# conventional matrix multiplication 3 by 3 matrix
# using symbolic not numeric evaluation.
# left & right are arrays in the calling routine with elements 1,1...3,3
upvar 1 $left l
upvar 1 $right r
variable m
foreach i {1 2 3} {
foreach j {1 2 3} {
set m($i,$j) ""
foreach k {1 2 3} { ;# add up the inner product terms.
if {$l($i,$k)=="" || $r($k,$j)==""} { ;# add nothing
append m($i,$j) ""
} elseif {$l($i,$k)==0 || $r($k,$j)==0} { ;# add nothing
append m($i,$j) ""
} elseif {$l($i,$k)==1} {
if {[string length $m($i,$j)]>0 && [string index $m($i,$j) end]!="+"} {append m($i,$j) "+"}
append m($i,$j) "$r($k,$j)"
} elseif {$r($k,$j)==1} {
if {[string length $m($i,$j)]>0 && [string index $m($i,$j) end]!="+"} {append m($i,$j) "+"}
append m($i,$j) "$l($i,$k)"
} else {
if {[string length $m($i,$j)]>0 && [string index $m($i,$j) end]!="+"} {append m($i,$j) "+"}
append m($i,$j) "($l($i,$k)*($r($k,$j)))"
}
}
}
}
foreach i {1 2 3} {
foreach j {1 2 3} {
# check replace "" with 0
if {$m($i,$j) eq ""} {set m($i,$j) 0}
}
}
return [array get m]
}
proc matprint {m} {
# prints the 3 by 3 matrix m.
# m is an array in the calling routine with elements 1,1...3,3
upvar 1 $m mat
foreach i {1 2 3} {
puts "$mat($i,1) | $mat($i,2) | $mat($i,3) "
}
}
proc evaluate {term args} {
# evaluate term substituting name-value pairs from args
# EG evaluate "sin(theta)" theta 1.1
# evaluates sin(1.1). There may be multiple values to substitute
# useful for complex substitutions as seen in the rotation matrix example
# NB this can also use formulae such as
# evaluate "sin(theta)" theta 2*atan(1)
# which becomes sin(2*atan(1))
# and the final expr in this proc evaluates the 2*atan as well(!)
# NB the expr cannot be braced as it is not composed of numerical variables but is a complex string.
foreach {name value} $args {
set term [regsub -all $name $term $value]
}
# nb no brackets as term modifies itself so cannot be compiled?.
return [expr $term]
}
proc getmatvalue {m args} {
# evaluate matrix substituting name-value pairs from args
# EG getmatvalue result theta 1.1 phi 2.4 psi 3.1
# evaluates each element of the matrix with theta = 1.1, phi 2.4 etc.
# NB can also evaluate as getmatvalue result theta 2*atan(1)
upvar 1 $m mat
variable mv
foreach i {1 2 3} {
foreach j {1 2 3} {
# evaluate each element with numeric substitutions
set mv($i,$j) [format "%8.5f" [eval evaluate $mat($i,$j) $args]]
}
}
return [array get mv]
}Examples edit
simple rotation matrix
array set mx [rotmat z psi] puts "\n=====\nResult of rotation by psi about Z axis" matprint mx cos(psi) | sin(psi) | 0 -sin(psi) | cos(psi) | 0 0 | 0 | 1
Product of 2 matrices
array set my [rotmat z theta] array set result [matmultiply mx my] puts "\n=====\nResult of rotation by psi about Z axis then by theta about z" matprint result cos(theta) | sin(theta) | 0 (cos(psi)*(-sin(theta))) | (cos(psi)*(cos(theta))) | sin(psi) (-sin(psi)*(-sin(theta))) | (-sin(psi)*(cos(theta))) | cos(psi)
Full 3 angle Euler matrix
array set mx [rotmat X psi] array set my [rotmat Y theta] array set mz [rotmat Z phi] array set result [matmultiply mx my] array set result [matmultiply result mz] puts "\n=====\nResult of multiplying in order XYZ = psi theta phi" matprint result (cos(theta)*(cos(phi))) | (cos(theta)*(sin(phi))) | -sin(theta) ((sin(psi)*(sin(theta)))*(cos(phi)))+(cos(psi)*(-sin(phi))) | ((sin(psi)*(sin(theta)))*(sin(phi)))+(cos(psi)*(cos(phi))) | (sin(psi)*(cos(theta))) ((cos(psi)*(sin(theta)))*(cos(phi)))+(-sin(psi)*(-sin(phi))) | ((cos(psi)*(sin(theta)))*(sin(phi)))+(-sin(psi)*(cos(phi))) | (cos(psi)*(cos(theta)))Compare this with Wolfram Encyclopedia lines 51-59. A means of simplifying the terms to remove brackets would be useful.
Evaluating this matrix with theta, phi, psi defined
set phi .2 set theta .3 set psi .4 puts " Matrix theta = $theta*atan(1) psi $psi, phi $phi" array set matvalues [getmatvalue result theta $theta*atan(1) psi $psi phi $phi] matprint matvalues 0.95299 | 0.19318 | -0.23345 -0.09389 | 0.92076 | 0.37866 0.28810 | -0.33894 | 0.89561
AMG: I suggest putting your matrices in nested lists instead of arrays. You can use [lindex] and [lset] to efficiently get and set elements inside nested lists. This will improve performance a little bit because there's no need for shimmering the indices to strings to get the hash table key.

