Documents/NamdSample/namd210/lib/abf/vectors.tcl (327 lines of code) (raw):

######################################################################### # # # (C) Copyright 1995-2003 The Board of Trustees of the # # University of Illinois # # All Rights Reserved # # # ######################################################################### ############################################################################ # RCS INFORMATION: # # $RCSfile: vectors.tcl,v $ # $Author: jim $ $Locker: $ $State: Exp $ # $Revision: 1.2 $ $Date: 2005/07/21 20:46:02 $ # ############################################################################ # DESCRIPTION: # These routines handle the vector and matrix manipulations needed for # doing 3D transformations. # ############################################################################ # This is part of the VMD installation. # For more information about VMD, see http://www.ks.uiuc.edu/Research/vmd # a vector is a n element list of numbers (in column form) # a matrix is a 4x4 matrix represeneted as a 4 element list of 4 # 4 elements, in row major form # package provide vectors 1.0.0 set M_PI 3.14159265358979323846 # Function: veczero # Returns : the zero vector, {0 0 0} proc veczero {} { return {0 0 0} } # Function: vecdist {vector v1} {vector v2} # Returns : || v2 - v1 || # This is syntactic sugar. proc vecdist {x y} { veclength [vecsub $x $y] } # Function: vecdot {vector x} {vector y} # Returns : the vector dot product v1 * v2 proc vecdot {x y} { if {[llength $x] != [llength $y]} { error "vecdot needs vectors of the same size: $x : $y" } set ret 0 foreach t1 $x t2 $y { set ret [expr $ret + $t1 * $t2] } return $ret } # Function: veccross {v1} {v2} # Returns : cross product of v1 and v2 proc veccross {x y} { # J. Henin, 11/2004 # lassign is not available in NAMD's Tcl interpreter # lassign $x x1 x2 x3 # lassign $y y1 y2 y3 foreach {x1 x2 x3} $x {} foreach {y1 y2 y3} $y {} # J. Henin, 11/2004 - end set ret {} lappend ret [expr $x2 * $y3 - $y2 * $x3] lappend ret [expr - $x1 * $y3 + $y1 * $x3] lappend ret [expr $x1 * $y2 - $y1 * $x2] return $ret } # Function: veclength2 {v} # Returns: the square of the vector length proc veclength2 {v} { set retval 0 foreach term $v { set retval [expr $retval + $term * $term] } return $retval } # Function: vecnorm {v} # Returns: the normal vector pointing along v proc vecnorm {v} { set sum [veclength $v] set retval {} foreach term $v { lappend retval [expr $term / $sum] } return $retval } # Function: vecinvert {v} # Returns: a vector with all terms inverted proc vecinvert {v} { set ret {} foreach i $v { lappend ret [expr -$i] } return $ret } # Function: coordtrans {matrix} {vector} # Returns : the vector = {matrix} * {vector} # If the matrix is 4x4 and the vector is length 3, the 4th element is 1 proc coordtrans {m v} { if { [llength $v] == 3} { lappend v 1 return [lrange [vectrans $m $v] 0 2] } return [vectrans $m $v] } # Function: transidentity # Returns: the identity matrix proc transidentity { } { return "{1.0 0.0 0.0 0.0} {0.0 1.0 0.0 0.0} {0.0 0.0 1.0 0.0} {0.0 0.0 0.0 1.0}" } # Function: transtranspose {matrix} # Returns : the transpose of the matrix, as a matrix -- must be 4x4 proc transtranspose {m} { lassign $m m1 m2 m3 m4 lassign $m1 m11 m12 m13 m14 lassign $m2 m21 m22 m23 m24 lassign $m3 m31 m32 m33 m34 lassign $m4 m41 m42 m43 m44 set retval {} lappend retval [concat $m11 $m21 $m31 $m41] lappend retval [concat $m12 $m22 $m32 $m42] lappend retval [concat $m13 $m23 $m33 $m43] lappend retval [concat $m14 $m24 $m34 $m44] return $retval } # Function: find_rotation_value <list reference> # Returns: value of the rotation in radians with the appropriate # list elements removed proc find_rotation_value {varname} { global M_PI upvar $varname a if {![info exists a]} { error "find_rotation_value: don't know upvar $varname" } set amount [expr [lvarpop a] + 0.0] set units [lvarpop a] if {$units == "rad" || $units == "radians" || $units == "radian"} { # set amount $amount } elseif {$units == "pi"} { set amount [expr $amount * $M_PI] } elseif {$units == "deg" || $units == "degrees" || $units == "degree"} { set amount [expr $amount / 180.0 * $M_PI] } else { if {$units != ""} { lvarpush a $units } # default is degrees set amount [expr $amount / 180.0 * $M_PI] } return $amount } # Function: transaxis {'x' | 'y' | 'z'} amount { | deg | rad | pi } # Returns: the matrix to rotate "amount" radians about the given axis # the default angle measurement is "degrees" proc transaxis {axis args} { global M_PI if { $axis != "x" && $axis != "y" && $axis != "z" } { error "transaxis must get either x, y, or z, not $axis" } set amount [find_rotation_value args] if { $args != ""} { error "Unknown angle measurement '$args' in transaxis" } set cos [expr cos($amount)] set mcos [expr -$cos] set sin [expr sin($amount)] set msin [expr -$sin] if { $axis == "x" } { set retval "{1.0 0.0 0.0 0.0}" lappend retval [concat 0.0 $cos $msin 0.0] lappend retval [concat 0.0 $sin $cos 0.0] lappend retval {0.0 0.0 0.0 1.0} return $retval } if { $axis == "y" } { set retval {} lappend retval [concat $cos 0.0 $sin 0.0] lappend retval {0.0 1.0 0.0 0.0} lappend retval [concat $msin 0.0 $cos 0.0] lappend retval {0.0 0.0 0.0 1.0} return $retval } if { $axis == "z" } { set retval {} lappend retval [concat $cos $msin 0.0 0.0] lappend retval [concat $sin $cos 0.0 0.0] lappend retval {0.0 0.0 1.0 0.0} lappend retval {0.0 0.0 0.0 1.0} return $retval } } # Function: transoffset <vector> # Returns: the matrix needed to translate by vector proc transoffset {v} { lassign $v x y z return "{1.0 0.0 0.0 $x} {0.0 1.0 0.0 $y} {0.0 0.0 1.0 $z} {0.0 0.0 0.0 1.0}" } # Function: transabout <vector> amount { | deg | rad | pi } # Returns: rotation matrix the given amount around the given axis proc transabout {axis args} { lassign $args amount units set transf [transvec $axis] set transfinv [transvecinv $axis] set rot [transaxis x $amount $units] return [transmult $transf $rot $transfinv] } # Function: trans # this has lots of options # proc trans {args} { set origin {0.0 0.0 0.0} set offset {0.0 0.0 0.0} set axis {1.0 0.0 0.0} set amount 0 set rotmat [transidentity] while { [set keyword [lvarpop args]] != ""} { if { $keyword == "origin" } { set origin [lvarpop args] continue } if { $keyword == "offset" } { set offset [lvarpop args] continue } if { $keyword == "center" } { set offset [lvarpop args] set origin $offset continue } # alias 'x' to 'axis x', 'y' to 'axis y', 'z' to 'axis z' if { $keyword == "x" || $keyword == "y" || $keyword == "z"} { lvarpush args $keyword set keyword "axis" } if { $keyword == "axis" } { set axis [lvarpop args] if {$axis == "x"} { set axis {1.0 0.0 0.0} } elseif {$axis == "y"} { set axis {0.0 1.0 0.0} } elseif {$axis == "z"} { set axis {0.0 0.0 1.0} } elseif {[llength $axis] != 3} { error "transform: axis must be 'x', 'y', 'z' or a vector, not $axis" } # find out how much to rotate set amount [find_rotation_value args] # and apply to the current rotation matrix set rotmat [transmult [transabout $axis $amount rad] $rotmat] set axis {1.0 0.0 0.0} set amount 0.0 continue } if { $keyword == "bond" } { set v1 [lvarpop args] set v2 [lvarpop args] set origin $v1 set offset $v1 set axis [vecsub $v2 $v1] # find out how much to rotate set amount [find_rotation_value args] # puts "Axis is: $axis" set rotmat [transabout $axis $amount rad] # puts "Rotmat is: $rotmat" continue } if { $keyword == "angle" } { set v1 [lvarpop args] set v2 [lvarpop args] set v3 [lvarpop args] set origin $v2 set offset $v2 set axis [veccross [vecsub $v2 $v1] [vecsub $v3 $v2]] if {[veclength $axis] <= 0.0} { if {[veclength [veccross [vecnorm [vecsub $v1 $v2]] {1.0 0.0 0.0}]] < 0.01} { set axis {0.0 0.0 1.0} puts "Warning: transform found degenerate 'angle'; using z axis" } else { set axis {1.0 0.0 0.0} puts "Warning: transform found degenerate 'angle'; using x axis" } } else { set axis [vecnorm $axis] } # find out how much to rotate set amount [find_rotation_value args] set rotmat [transabout $axis $amount rad] continue } error "Unknown command for 'transform': $keyword" } # end of while loop set origmat [transoffset [vecinvert $origin]] set offsetmat [transoffset $offset] # puts "Orig: $origmat" # puts "Offset: $offsetmat" # puts "Rotmat: $rotmat" # puts [list Result: [transmult $offsetmat $rotmat $origmat]] return [transmult $offsetmat $rotmat $origmat] } # end of "transform" # Function: trans_from_rotate # Returns a transformation matrix given a 3x3 rotation matrix proc trans_from_rotate {rotate} { lassign $rotate a b c return "{$a 0} {$b 0} {$c 0} {0 0 0 1}" } # Function: trans_to_rotate # Returns: the upper left 3x3 rotation component proc trans_to_rotate {trans_matrix} { lassign $trans_matrix a b c lassign $a a1 a2 a3 lassign $b b1 b2 b3 lassign $c c1 c2 c3 return "{$a1 $a2 $a3} {$b1 $b2 $b3} {$c1 $c2 $c3}" } # Function: trans_from_offset # Returns: the transformation corresponding to an offset vector proc trans_from_offset {offset} { return [transoffset $offset] } # Function: trans_to_offset # Returns: the transformation offset of the given matrix proc trans_to_offset {trans_matrix} { return [coordtrans $trans_matrix {0 0 0}] } # set nothing "Okay!"