This is a butcher job on the
Moving an anthropomorphous robot arm.
I needed a simpler example for my students--this is a first attempt.
proc deg2rad { angle_degrees } {expr {($angle_degrees)*0.0174532925199}}
proc rad2deg { angle_radians } {expr {($angle_radians)*57.2957795131}}
proc math_sin_cos { angle nickname } {
uplevel [list set cos_$nickname [expr {cos(($angle))}]]
uplevel [list set sin_$nickname [expr {sin(($angle))}]]
}
proc matrix_rotation_z { angle } {
math_sin_cos $angle t
list $cos_t $sin_t 0 0 [expr -$sin_t] $cos_t 0 0 0 0 1 0 0 0 0 1
}
proc matrix_rotation_x { angle } {
math_sin_cos $angle t
list 1 0 0 0 0 $cos_t $sin_t 0 0 [expr -$sin_t] $cos_t 0 0 0 0 1
}
proc matrix_rotation_y { angle } {
math_sin_cos $angle t
list $cos_t 0 $sin_t 0 0 1 0 0 [expr -$sin_t] 0 $cos_t 0 0 0 0 1
}
proc matrix_translation {x y z} {
#list 1 0 0 $x 0 1 0 $y 0 0 1 $z 0 0 0 1
list 1 0 0 0 0 1 0 0 0 0 1 0 $x $y $z 1
}
proc matrix_identity {} {
list 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
}
proc matrix_multiply { matA matB } {
# use tcl trick to assign these variables
foreach {b11 b12 b13 b14 b21 b22 b23 b24 b31 b32 b33 b34 b41 b42 b43 b44} $matB {}
set result {}
foreach {x y z w} $matA {
lappend result \
[expr {$x*$b11+$y*$b21+$z*$b31+$w*$b41}] \
[expr {$x*$b12+$y*$b22+$z*$b32+$w*$b42}] \
[expr {$x*$b13+$y*$b23+$z*$b33+$w*$b43}] \
[expr {$x*$b14+$y*$b24+$z*$b34+$w*$b44}]
}
return $result
}
proc apply_current_matrix { coords } {
set ::currentmatrix [matrix_multiply $::worldmatrix $::viewmatrix]
# use tcl trick to assign these variables
foreach {a11 a12 a13 a14 a21 a22 a23 a24 a31 a32 a33 a34 a41 a42 a43 a44} $::currentmatrix {}
set result {}
foreach {x y z ONE} $coords {
lappend result \
[expr {$x*$a11+$y*$a21+$z*$a31+ $a41}] \
[expr {$x*$a12+$y*$a22+$z*$a32+ $a42}] \
[expr {$x*$a13+$y*$a23+$z*$a33+ $a43}] \
1
}
set coords $result
set result {}
foreach { x y z o } $coords {
if { $::perspective == 0 } {
lappend result [expr $x + $::screencenter_x] \
[expr $::screencenter_y - $y]
} else {
set z [expr $z + 1500]
if { $z == 0 } { set z 1 }
lappend result [expr $x/$z*1200 + $::screencenter_x] \
[expr $::screencenter_y - $y/$z*1200]
}
}
return $result
}
## ------------------------------------------------------------
## Basic graphical elements.
## ------------------------------------------------------------
proc draw_3D_line { xyzcoords {obj_tag {}} {tags {}} } {
set coords [apply_current_matrix $xyzcoords]
if { ![info exists ::id($obj_tag)] } {
set ::id($obj_tag) [$::maincanvas create line $coords -tags \
[lappend tags $obj_tag] ]
} else {
$::maincanvas coords $::id($obj_tag) $coords
}
}
proc draw_prism { prism_tag xsize ysize zsize \
xoffset yoffset zoffset {tags Robot} } {
foreach {face coords} {
A {-1 -1 -1 1 1 -1 -1 1 1 1 -1 1 -1 1 -1 1 -1 -1 -1 1}
B {-1 -1 1 1 1 -1 1 1 1 1 1 1 -1 1 1 1 -1 -1 1 1}
C {-1 -1 -1 1 -1 -1 1 1 -1 1 1 1 -1 1 -1 1 -1 -1 -1 1}
D { 1 -1 -1 1 1 -1 1 1 1 1 1 1 1 1 -1 1 1 -1 -1 1}
} {
set results {}
foreach {x y z one} $coords {
lappend results \
[expr $x * $xsize + $xoffset] \
[expr $y * $ysize + $yoffset] \
[expr $z * $zsize + $zoffset] \
1
}
set coords $results
draw_3D_line $coords ${prism_tag}_$face $tags
}
}
proc draw_plane { obj_tag indices {tags {}} } {
set delta 30.0
set num 11
set max [expr {$delta*($num-1)}]
set coords { 0 0 0 1 0 0 0 1}
set counter 0
foreach { a b c } $indices {
set vector $coords
set plane_tag ${obj_tag}_[incr counter]
for {set x 0.0} {$x <= $max} {set x [expr {$x+$delta}]} {
lset vector $a $x
lset vector $b $x
lset vector $c $max
draw_3D_line $vector ${plane_tag}_$x [concat [list $obj_tag] $tags]
}
}
}
## ------------------------------------------------------------
## TK options.
## ------------------------------------------------------------
option add *command_buttons.exit.text "Exit"
## ------------------------------------------------------------
## Widget procedures.
## ------------------------------------------------------------
proc create_scale { linkname master varname {from 0} {to 90} {default 0}} {
set ::mem($varname.from) $from
set ::mem($varname.to) $to
set ::mem($varname.default) $default
set $varname $default
set f [labelframe $master.$varname -class [string totitle $varname]]
set column_index 0
label [set label_widget $f.lab_$varname] -text $linkname
scale [set scale_widget $f.$varname] \
-from $::mem($varname.from) -to $::mem($varname.to)
$scale_widget set $::mem($varname.default)
$scale_widget configure -command \
[list update_parameter_from_scale_widget $varname]
grid $label_widget -column $column_index -row 0 -sticky news
grid $scale_widget -column $column_index -row 1 -sticky news
incr column_index
return $f
}
proc update_parameter_from_scale_widget { param_name param_value } {
set $param_name $param_value
after 0 update_and_draw
}
## ------------------------------------------------------------
## Main procedures.
## ------------------------------------------------------------
proc main_exit {} {exit}
proc main_program {} {
widget_setup_defaults
# configure_toplevel
wm geometry . +20+20
wm title . "Simple 3D Robot Arm"
grid columnconfigure . 0 -weight 1
grid rowconfigure . 0 -weight 1
set ::maincanvas [canvas .drawing -width 600 -height 600 -bg #f8f8f8]
frame .controlsFrame
grid $::maincanvas .controlsFrame -sticky news
set camerabox [build_camera_controls .controlsFrame]
set robotbox [build_robot_controls .controlsFrame]
grid $camerabox -sticky news
grid $robotbox -sticky news
set ::bp [button .controlsFrame.pers -command toggle_perspective]
$::bp config -text "Perspective is On"
grid $::bp
grid [button .controlsFrame.exit -command main_exit -text "Exit"]
update_and_draw
configure_drawing ;# do this once
}
proc toggle_perspective {} {
if {$::perspective} {
set ::perspective 0
$::bp config -text "Perspective is Off"
} else {
set ::perspective 1
$::bp config -text "Perspective is On"
}
after 0 update_and_draw
}
proc set_worldmatrix {mat} {
set ::worldmatrix $mat
}
proc set_viewmatrix {mat} {
set ::viewmatrix $mat
}
proc mult_worldmatrix {mat} {
set ::worldmatrix [matrix_multiply $mat $::worldmatrix]
}
proc mult_viewmatrix {mat} {
set ::viewmatrix [matrix_multiply $mat $::viewmatrix]
}
proc get_worldmatrix {} {
return $::worldmatrix
}
proc get_viewmatrix {} {
return $::viewmatrix
}
#============================================================
proc update_and_draw {} {
set_viewmatrix [matrix_rotation_x [deg2rad $::cam_angle1]]
mult_viewmatrix [matrix_rotation_y [deg2rad $::cam_angle2]]
set_worldmatrix [matrix_identity]
# draw workspace -----------------------------------
draw_plane WorkSpace_XY { 0 4 5 1 5 4 } [list WorkSpace]
draw_plane WorkSpace_YZ { 1 5 6 2 6 5 } [list WorkSpace]
draw_plane WorkSpace_ZX { 2 6 0 4 0 6 } [list WorkSpace]
draw_3D_line {0 0 0 1 300 0 0 1} XAXIS
draw_3D_line {0 0 0 1 0 300 0 1} YAXIS
draw_3D_line {0 0 0 1 0 0 300 1} ZAXIS
### move this
set ::mem(link0) [matrix_rotation_y [deg2rad $::theta_base]]
set ::mem(link1) [matrix_rotation_x [deg2rad $::anngle1]]
set ::mem(link2) [matrix_rotation_x [deg2rad $::anngle2]]
set ::mem(link3) [matrix_rotation_x [deg2rad $::anngle3]]
set ::mem(link4) [matrix_rotation_z [deg2rad $::theta_hand]]
set ::mem(left5) [matrix_translation $::sllde5 0 0]
set ::mem(right5) [matrix_translation [expr -$::sllde5] 0 0]
# draw robot ---------------------------------------
set_worldmatrix [matrix_translation 200 0 150]
mult_worldmatrix $::mem(link0)
# draw base
draw_prism base 40 50 40 -40 50 0
mult_worldmatrix [matrix_translation 5 75 0]
mult_worldmatrix $::mem(link1)
# draw link 1
draw_prism part1 5 10 10 0 0 0
draw_prism part2 10 10 50 15 0 30
mult_worldmatrix [matrix_translation 0 0 80]
mult_worldmatrix $::mem(link2)
# draw link 2
draw_prism part3 5 10 10 0 0 0
draw_prism part4 10 10 50 -15 0 30
mult_worldmatrix [matrix_translation 0 0 80]
mult_worldmatrix $::mem(link3)
# draw link 3
draw_prism part5 5 10 10 0 0 0
draw_prism part6 10 10 50 15 0 30
mult_worldmatrix [matrix_translation 15 0 85]
mult_worldmatrix $::mem(link4)
draw_prism part7 10 10 5 0 0 0
draw_prism part8 60 10 10 0 0 15
set savemat [get_worldmatrix] ;# save current matrix
mult_worldmatrix $::mem(left5)
draw_prism lefthand 10 10 50 10 0 75
set_worldmatrix $savemat ;# restore previous matrix
mult_worldmatrix $::mem(right5)
draw_prism righthand 10 10 50 -10 0 75
}
proc build_robot_controls { master } {
set frame0 [labelframe $master.robot -text "Robot Controls"]
grid \
[create_scale base $frame0 ::theta_base 180 -180 0] \
[create_scale arm1 $frame0 ::anngle1 180 -180 0] \
[create_scale arm2 $frame0 ::anngle2 180 -180 0] \
[create_scale arm3 $frame0 ::anngle3 180 -180 0] \
-sticky news
grid \
[create_scale handspin $frame0 ::theta_hand 90 -90 0] \
[create_scale grasp $frame0 ::sllde5 40 0 40] \
-sticky news
return $frame0
}
proc build_camera_controls { master } {
set f [labelframe $master.camera -text "Camera Controls"]
grid \
[create_scale psi $f ::cam_angle1 180 -180 -22] \
[create_scale phi $f ::cam_angle2 180 -180 -150] \
-sticky news
return $f
}
proc configure_drawing {} {
$::maincanvas itemco WorkSpace -fill #00bb00
$::maincanvas itemco Robot -width 2
$::maincanvas itemco XAXIS -fill red
$::maincanvas itemco YAXIS -fill #008800
$::maincanvas itemco ZAXIS -fill blue
}
proc widget_setup_defaults {} {
set ::screencenter_x 300
set ::screencenter_y 400
set ::perspective 1
option add *Workspace.text "Camera"
option add *Robot.text "Robot"
}
main_program