MBS - After GS posted 3D polyhedra with simple tk canvas I decided to re-visit some code that I had started last year but never finished. That code had started out as a 'port' of a simple 3d graphics code from a class I had taken years ago.
The resulting code, 3dviewer, has little in common with the original and I've borrowed some code and data from 3D polyhedra with simple tk canvas.
Many thanks to GS.
I hope that most of it is fairly intuitive. If it isn't, please add to this page anything that you feel will help future users.
I had hoped to add more 'documentation' before posting this, but it appears that work is going to keep me very busy for the next month or two, so I'm posting in its current state. If things are not clear, just ask...
Within this viewer, the coordinates of the model being viewed are left unchanged and the Camera (eye position) is moved to different locations in order to view the model.
A spherical coordinate system is used to position the camera. Basically the coordinates of camera are determined by setting rho, theta, and phi, where
For more information regarding spherical corrdinate systems, here are some sites to try : [L1 ], [L2 ], and [L3 ].
Basic Viewer Controls :
The Dials can be used to change either the camera position or the position of the light.
Use Button One (left) to 'click and drag' the arrow within the dial to change the Azimuth and/or Elevation of either the 'Camera Position' or 'Light Position'
Within the 'Camera Position' controls
Use the sliders to change the value of either rho (described above) or distance (distance to viewing plane). Basically they can both be used to 'zoom' in or out.
You can also change the camera position by 'clicking and dragging' the mouse within the graphics display area
Use <Button-1> to rotate the image Use <Button-2> to pan (move) the image Use <Button-3> to zoom in or out
I'm using the same data format that GS used within 3D polyhedra with simple tk canvas.
Basically, define a list coordinates, 'vertices' and then another list that defines the connectivity, 'how to connect the vertices'. For example, the following describes a cube:
set lvtx {{0.7 0.7 0.7} {-0.7 0.7 0.7} {-0.7 -0.7 0.7} {0.7 -0.7 0.7} {0.7 0.7 -0.7} {-0.7 0.7 -0.7} {-0.7 -0.7 -0.7} {0.7 -0.7 -0.7}} set lcnx {{4 7 6 5} {0 1 2 3} {3 2 6 7} {4 5 1 0} {0 3 7 4} {5 6 2 1}}
#! /bin/env tclsh package require Tk # # 3dviewer.tcl # # Author : Mark B. Stucky # Description : Simple 3D viewer using just the Tk canvas # # Initialize the user interface # proc Init { } { global gvar wm title . "3dviewer" set screenWidth [winfo screenwidth .] set screenHeight [winfo screenheight .] set sc [min $screenWidth $screenHeight] set scx [expr {$sc * 0.5}] set scy $scx set gvar(Xscreen) $scx set gvar(Yscreen) $scy set gvar(rho) 5. set gvar(theta) 0.0 set gvar(phi) 0.0 set gvar(dist) 500.0 set gvar(x,translate) 0.0 set gvar(y,translate) 0.0 set gvar(hlite) 0 set gvar(backfacecull) 1 set gvar(zsort) 1 set gvar(daxis) 1 set gvar(look_at) [list 0.0 0.0 0.0] set pi [expr {4.0 * atan(1.0)}] set mpi [expr {-1.0 * $pi}] set gvar(pi) $pi set gvar(mpi) $mpi set gvar(twopi) [expr {2.0 * $pi}] set bg \#4f4f4f set fg grey set gvar(light_coord) [list 10.0 10.0 -10.0] set gvar(theta_light) 0.0 set gvar(phi_light) 0.0 # build up the list of "known objects" to display. (These are # connected to buttons on the interface) # set gvar(obj_list) [list pyramid cube octahedron icosahedron head obj_File from_File] ######################################################################## canvas .c -width $scx -height $scy -bg black -bd 0 pack .c -side bottom set gvar(canv) .c bind .c <ButtonPress-1> {canvas_rotate_start %W %x %y} bind .c <B1-Motion> {canvas_rotate_drag %W %x %y} bind .c <ButtonRelease-1> {canvas_rotate_end %W %x %y} bind .c <ButtonPress-2> {canvas_pan_start %W %x %y} bind .c <B2-Motion> {canvas_pan_drag %W %x %y} bind .c <ButtonRelease-2> {canvas_pan_end %W %x %y} bind .c <ButtonPress-3> {canvas_zoom_start %W %x %y} bind .c <B3-Motion> {canvas_zoom_drag %W %x %y} bind .c <ButtonRelease-3> {canvas_zoom_end %W %x %y} ######################################################################## labelframe .f0 -text "Select Data" -padx 4 -pady 4 -height 100 foreach obj $gvar(obj_list) { button .f0.$obj -text $obj -command "ReadData $obj; redraw" -width 10 pack .f0.$obj -side top } labelframe .f1 -text "Camera Position" -padx 4 -pady 4 -height 100 label .f1.lrho -text "rho" label .f1.lthet -text "theta" label .f1.lphi -text "phi" label .f1.ldist -text "distance" scale .f1.rho -from 1.0 -to 100.0 -orient horizontal -resolution 1.0 \ -variable gvar(rho) -orient vertical -command set_rho scale .f1.dist -from 10.0 -to 20000.0 -orient horizontal -resolution 1.0 \ -variable gvar(dist) -orient vertical -command set_dist set gvar(dtheta) [dial::dial .f1.dthet 50 "Azimuth" $bg $fg {get_theta } ] set gvar(dazmth) [dial::dial .f1.dphi 50 "Elevation" $bg $fg {get_phi } ] grid .f1.rho -row 0 -column 0 grid .f1.lrho -row 2 -column 0 grid $gvar(dtheta) -row 0 -column 1 grid $gvar(dazmth) -row 0 -column 2 grid .f1.dist -row 0 -column 3 grid .f1.ldist -row 2 -column 3 labelframe .f2 -text "Light Position" -padx 4 -pady 4 -height 100 set gvar(ltheta) [dial::dial .f2.lthet 50 "Azimuth" $bg $fg {get_theta_L } ] set gvar(lazmth) [dial::dial .f2.lphi 50 "Elevation" $bg $fg {get_phi_L } ] grid $gvar(ltheta) -row 0 -column 1 grid $gvar(lazmth) -row 0 -column 2 labelframe .f3 -text "Viewing Options" -padx 4 -pady 4 -height 100 checkbutton .f3.bcull -text "Backface culling" -variable gvar(backfacecull) checkbutton .f3.zsort -text "Shaded Faces" -variable gvar(zsort) checkbutton .f3.hlite -text "Light follows Camera" -variable gvar(hlite) checkbutton .f3.daxis -text "Display Axis" -variable gvar(daxis) grid .f3.bcull -row 0 -column 0 -sticky w grid .f3.zsort -row 1 -column 0 -sticky w grid .f3.hlite -row 2 -column 0 -sticky w grid .f3.daxis -row 3 -column 0 -sticky w pack .f0 -side left -fill y pack .f1 -side left -fill y pack .f2 -side left -fill y pack .f3 -side left -fill y } # The user changed the value of "rho" via the scale... # Update the display. # proc set_rho {val} { global gvar set gvar(rho) $val redraw } # The user changed the value of "dist" via the scale... # Update the display. # proc set_dist {val} { global gvar set gvar(dist) $val redraw } # # Return the min of two values # proc min { val1 val2 } { if { $val1 <= $val2 } { return $val1 } else { return $val2 } } # # Return the max of two values # proc max { val1 val2 } { if { $val1 >= $val2 } { return $val1 } else { return $val2 } } # Get the coords of the center of the face # proc Barycenter3D {verts edge} { set X 0 set Y 0 set Z 0 set n [llength $edge] foreach node $edge { set vtx [lindex $verts $node] foreach {x y z} $vtx { set X [expr {$X + $x}] set Y [expr {$Y + $y}] set Z [expr {$Z + $z}] } } return [list [expr {$X/$n}] [expr {$Y/$n}] [expr {$Z/$n}]] } #******************************* MatrixVectorProduct ***************** proc MatrixVectorProduct {M V} { set x [lindex $V 0] set y [lindex $V 1] set z [lindex $V 2] set w [lindex $V 3] return [list \ [expr {[lindex $M 0 0]*$x+[lindex $M 1 0]*$y+[lindex $M 2 0]*$z+[lindex $M 3 0]*$w}] \ [expr {[lindex $M 0 1]*$x+[lindex $M 1 1]*$y+[lindex $M 2 1]*$z+[lindex $M 3 1]*$w}] \ [expr {[lindex $M 0 2]*$x+[lindex $M 1 2]*$y+[lindex $M 2 2]*$z+[lindex $M 3 2]*$w}] \ [expr {[lindex $M 0 3]*$x+[lindex $M 1 3]*$y+[lindex $M 2 3]*$z+[lindex $M 3 3]*$w}] ] } #******************************* d_move ******************************** proc d_move { x y z w } { global gvar set tmp [MatrixVectorProduct $gvar(Curnt_Trans) [list $x $y $z $w]] foreach {xt yt zt wt} $tmp {break} if {$zt == 0.0} { set zt 0.000001 } set x1 [expr {($gvar(Xscreen)/2.) + round($gvar(dist)*($xt/$zt)) }] set y1 [expr {($gvar(Yscreen)/2.) - round($gvar(dist)*($yt/$zt)) }] set x1 [expr {$x1 + $gvar(x,translate) }] set y1 [expr {$y1 - $gvar(y,translate) }] set gvar(Curnt_X_position) $x1 set gvar(Curnt_Y_position) $y1 return [list $x1 $y1] } #******************************* d_draw ******************************** proc d_draw {x y z w {color white} {arrow none} } { global gvar set tmp [MatrixVectorProduct $gvar(Curnt_Trans) [list $x $y $z $w]] foreach {xt yt zt} $tmp {break} if {$zt == 0.0} { set zt 0.000001 } set x2 [expr {($gvar(Xscreen)/2.) + round($gvar(dist)*($xt/$zt)) }] set y2 [expr {($gvar(Yscreen)/2.) - round($gvar(dist)*($yt/$zt)) }] set x2 [expr {$x2 + $gvar(x,translate) }] set y2 [expr {$y2 - $gvar(y,translate) }] $gvar(canv) create line [list $gvar(Curnt_X_position) $gvar(Curnt_Y_position) $x2 $y2] \ -fill $color -arrow $arrow set gvar(Curnt_X_position) $x2 set gvar(Curnt_Y_position) $y2 return [list $x2 $y2] } #******************************* draw_shaded ******************************** proc draw_shaded {clist {fillcolor blue} {edgecolor white} } { global gvar set poly {} foreach {x y z} $clist { set w 1.0 set tmp [MatrixVectorProduct $gvar(Curnt_Trans) [list $x $y $z $w]] foreach {xt yt zt} $tmp {break} if {$zt == 0.0} { puts "zt = 0.0" set zt 0.001 } set x2 [expr {($gvar(Xscreen)/2.) + round($gvar(dist)*($xt/$zt)) }] set y2 [expr {($gvar(Yscreen)/2.) - round($gvar(dist)*($yt/$zt)) }] set x2 [expr {$x2 + $gvar(x,translate) }] set y2 [expr {$y2 - $gvar(y,translate) }] lappend poly $x2 $y2 } if {$edgecolor == "none"} { $gvar(canv) create polygon $poly -fill $fillcolor } else { $gvar(canv) create polygon $poly -fill $fillcolor -outline $edgecolor } } #*********************** Set_Viewing_Transform ******************************** # proc Set_Viewing_Transform { rho theta phi dist } { global gvar set sinth [expr {sin($theta)}] set costh [expr {cos($theta)}] set sinph [expr {sin($phi)}] set cosph [expr {cos($phi)}] set Ct {{0. 0. 0. 0.} {0. 0. 0. 0.} {0. 0. 0. 0.} {0. 0. 0. 0.}} lset Ct 0 0 [expr {-$sinth}] lset Ct 0 1 [expr {-$costh * $cosph}] lset Ct 0 2 [expr {-$costh * $sinph}] lset Ct 0 3 0.0 lset Ct 1 0 [expr {$costh}] lset Ct 1 1 [expr {-$sinth * $cosph}] lset Ct 1 2 [expr {-$sinth * $sinph}] lset Ct 1 3 0.0 lset Ct 2 0 0.0 lset Ct 2 1 $sinph lset Ct 2 2 [expr {-$cosph}] lset Ct 2 3 0.0 lset Ct 3 0 0.0 lset Ct 3 1 0.0 lset Ct 3 2 $rho lset Ct 3 3 1.0 set gvar(Curnt_Trans) $Ct set eye_x [expr {$rho * $sinph * $costh}] set eye_y [expr {$rho * $sinph * $sinth}] set eye_z [expr {$rho * $cosph}] set gvar(eye_point) [list $eye_x $eye_y $eye_z] } # proc Print_4x4_Mat {mat} { puts " [format "%14.7f %14.7f %14.7f %14.7f" \ [lindex $mat 0 0] [lindex $mat 0 1] [lindex $mat 0 2] [lindex $mat 0 3] ]" puts " [format "%14.7f %14.7f %14.7f %14.7f" \ [lindex $mat 1 0] [lindex $mat 1 1] [lindex $mat 1 2] [lindex $mat 1 3] ]" puts " [format "%14.7f %14.7f %14.7f %14.7f" \ [lindex $mat 2 0] [lindex $mat 2 1] [lindex $mat 2 2] [lindex $mat 2 3] ]" puts " [format "%14.7f %14.7f %14.7f %14.7f" \ [lindex $mat 3 0] [lindex $mat 3 1] [lindex $mat 3 2] [lindex $mat 3 3] ]" } proc Conc_Trans { a b c } { for {set i 0} {$i < 4} {incr i} { set xa [lindex $a $i 0] set ya [lindex $a $i 1] set za [lindex $a $i 2] set wa [lindex $a $i 3] for {set j 0} {$j < 4} {incr j} { set xb [lindex $b 0 $j] set yb [lindex $b 1 $j] set zb [lindex $b 2 $j] set wb [lindex $b 3 $j] lset c $i $j [expr {$xa*$xb + $ya*$yb + $za*$zb + $wa*$wb}] } } return $c } #********************************* Get_Vector ********************** proc Get_Vector { v1 v2 } { set x1 [lindex $v1 0] set y1 [lindex $v1 1] set z1 [lindex $v1 2] set x2 [lindex $v2 0] set y2 [lindex $v2 1] set z2 [lindex $v2 2] set ux [expr {$x2 - $x1 }] set uy [expr {$y2 - $y1 }] set uz [expr {$z2 - $z1 }] return [list $ux $uy $uz] } #********************************* CrossProduct **************** proc Vector_CrossProduct {v1 v2} { set x1 [lindex $v1 0] set y1 [lindex $v1 1] set z1 [lindex $v1 2] set x2 [lindex $v2 0] set y2 [lindex $v2 1] set z2 [lindex $v2 2] set normal [list [expr {$y1*$z2 - $y2*$z1}] \ [expr {$z1*$x2 - $z2*$x1}] \ [expr {$x1*$y2 - $x2*$y1}]] return $normal } #********************************* CrossProduct **************** proc CrossProduct {x1 y1 z1 x2 y2 z2} { set normal [list [expr {$y1*$z2 - $y2*$z1}] \ [expr {$z1*$x2 - $z2*$x1}] \ [expr {$x1*$y2 - $x2*$y1}]] return $normal } #********************************* DotProduct **************** proc Vector_DotProduct {v1 v2} { set x1 [lindex $v1 0] set y1 [lindex $v1 1] set z1 [lindex $v1 2] set x2 [lindex $v2 0] set y2 [lindex $v2 1] set z2 [lindex $v2 2] set mag [expr {$x1*$x2 + $y1*$y2 + $z1*$z2}] return $mag } proc DotProduct {x1 y1 z1 x2 y2 z2} { set mag [expr {$x1*$x2 + $y1*$y2 + $z1*$z2}] return $mag } proc Normal_Vector { v1 v2 } { set n [Vector_CrossProduct $v1 $v2] set mag [Vector_DotProduct $n $n] set mag [expr {sqrt($mag)}] foreach {x y z} $n {break} set xn [expr {$x / $mag }] set yn [expr {$y / $mag }] set zn [expr {$z / $mag }] set norm [list $xn $yn $zn] return $norm } #***************************** R E A D D A T A ****************************** proc ReadData {obj} { global gvar set gvar(display) "none" switch $obj { pyramid { set lvtx {{-1.0 -1.0 -1.0} {-1.0 1.0 -1.0} {1.0 1.0 -1.0} {1.0 -1.0 -1.0} {0.0 0.0 1.0}} set lcnx {{0 1 2 3} {1 0 4} {2 1 4} {3 2 4} {0 3 4}} set obj [list $lvtx $lcnx] } cube { set lvtx {{0.7 0.7 0.7} {-0.7 0.7 0.7} {-0.7 -0.7 0.7} {0.7 -0.7 0.7} {0.7 0.7 -0.7} {-0.7 0.7 -0.7} {-0.7 -0.7 -0.7} {0.7 -0.7 -0.7}} set lcnx {{4 7 6 5} {0 1 2 3} {3 2 6 7} {4 5 1 0} {0 3 7 4} {5 6 2 1}} set obj [list $lvtx $lcnx] } octahedron { set lvtx {{1 0 0} {0 1 0} {-1 0 0} {0 -1 0} {0 0 1} {0 0 -1}} set lcnx {{0 1 4} {1 2 4} {2 3 4} {3 0 4} {1 0 5} {2 1 5} {3 2 5} {0 3 5}} set obj [list $lvtx $lcnx] } icosahedron { set X 0.525731112119133606 set Z 0.850650808352039932 set lvtx [list [list -$X 0.0 $Z] [list $X 0.0 $Z] [list -$X 0.0 -$Z] \ [list $X 0.0 -$Z] [list 0.0 $Z $X] [list 0.0 $Z -$X] \ [list 0.0 -$Z $X] [list 0.0 -$Z -$X] [list $Z $X 0.0] \ [list -$Z $X 0.0] [list $Z -$X 0.0] [list -$Z -$X 0.0]] set lcnx {{4 0 1} {9 0 4} {5 9 4} {5 4 8} {8 4 1} {10 8 1} {3 8 10} {3 5 8} {2 5 3} {7 2 3} {10 7 3} {6 7 10} {11 7 6} {0 11 6} {1 0 6} {1 6 10} {0 9 11} {11 9 2} {2 9 5} {2 7 11}} set obj [list $lvtx $lcnx] } shuttle { source shuttle.dat set obj [list $lvtx $lcnx] } torus { source torus.dat set obj [list $lvtx $lcnx] } head { set lvtx { {0.0815164 1.0563 0.553971} {-0.176308 1.03867 0.48761} {-0.17315 0.990498 0.281613} {-0.058315 1.06426 0.587988} {0.211583 0.99314 0.36804} {-0.313551 0.957059 0.341798} {-0.317535 0.992361 0.592713} {0.0518048 1.05141 0.729255} {-0.0513244 0.959933 0.134707} {-0.149733 1.0415 0.722784} {0.404957 0.922132 0.470722} {0.229103 1.02096 0.637104} {-0.474444 0.872661 0.427004} {-0.00397248 1.00454 0.928927} {0.151933 0.937974 0.125533} {-0.346079 0.839796 -0.0407652} {-0.226811 0.907029 0.0577824} {-0.317147 0.977994 0.771794} {-0.0963216 0.909944 -0.0395656} {-0.222774 0.975565 0.915396} {0.0750434 0.908138 -0.0386818} {0.357723 0.966722 0.724688} {0.216185 0.99778 0.847467} {-0.451074 0.905819 0.728981} {-0.390356 0.897425 0.917227} {0.604318 0.773103 0.555129} {0.375942 0.827882 0.0783841} {-0.511651 0.726258 0.0540681} {0.240711 0.862129 -0.0609565} {0.11932 0.893634 1.13431} {-0.607315 0.734143 0.405266} {-0.112733 0.916325 1.09691} {-0.177902 0.881524 -0.156274} {-0.00344113 0.895856 -0.214912} {0.525275 0.787942 0.298269} {0.496117 0.880631 0.690745} {0.428777 0.889842 0.909399} {0.187307 0.862699 -0.252254} {-0.139972 0.889269 -0.354547} {-0.584059 0.791153 0.640671} {0.64705 0.654417 0.331245} {0.228772 0.938052 1.01422} {0.00760786 0.780799 1.26544} {-0.532337 0.80994 0.851687} {-0.312894 0.852521 1.08829} {-0.307563 0.840343 -0.373613} {-0.200751 0.759027 1.24815} {0.0958251 0.899797 -0.458735} {-0.695697 0.621771 0.574836} {-0.676604 0.575295 0.302026} {0.540869 0.661448 0.0514482} {0.349692 0.778993 -0.238376} {-0.158722 0.92195 -0.615584} {0.720494 0.605552 0.549952} {0.580701 0.795469 0.789724} {-0.655999 0.665372 0.782537} {-0.474337 0.765886 1.04608} {-0.429926 0.752768 -0.243574} {0.343623 0.817141 1.13707} {0.290001 0.826865 -0.488448} {0.0393123 0.942363 -0.649674} {0.749562 0.464036 0.345849} {0.223102 0.73433 1.27981} {-0.764232 0.392638 0.499342} {-0.7254 0.378121 0.217378} {-0.598074 0.663379 0.975876} {-0.339081 0.864093 -0.629725} {0.602755 0.718899 0.943385} {0.505742 0.736369 1.08213} {-0.527588 0.629322 -0.180958} {-0.410445 0.782697 -0.463291} {-0.257316 0.563259 1.36276} {0.790025 0.3953 0.53096} {0.687256 0.477247 0.156046} {0.683324 0.668813 0.76848} {-0.0701037 0.594441 1.38938} {-0.0418051 1.00574 -0.868669} {-0.629367 0.50156 0.0385379} {0.76263 0.504897 0.739177} {0.596093 0.490119 -0.092017} {-0.745746 0.449625 0.772078} {-0.691077 0.511744 0.916691} {-0.390929 0.683132 1.21129} {0.809491 0.284134 0.320618} {0.484394 0.646736 -0.200834} {0.442113 0.587435 1.27531} {0.228901 0.896371 -0.670331} {0.122213 0.622299 1.37993} {-0.772301 0.306435 0.282382} {-0.767225 0.102806 0.56705} {-0.512632 0.648742 -0.435018} {-0.261039 0.950306 -0.823113} {0.607748 0.59308 1.10186} {0.459587 0.658772 -0.475296} {0.149554 0.973365 -0.835847} {0.125737 0.420994 1.46367} {-0.773726 0.207325 0.385801} {-0.701304 0.341193 0.0960399} {-0.520099 0.599444 1.15838} {-0.428418 0.866119 -0.826927} {0.796038 0.265628 0.720585} {0.707517 0.55191 0.935356} {0.389078 0.773561 -0.683573} {0.320546 0.893295 -0.857738} {-0.0294176 1.12923 -1.16284} {-0.79432 0.232842 0.329046} {-0.848297 0.279976 0.314515} {-0.843522 0.33619 0.261506} {-0.603155 0.41874 -0.157673} {-0.631816 0.443099 1.09242} {-0.577648 0.475366 -0.408503} {-0.472447 0.76812 -0.671233} {-0.194617 1.05493 -1.03655} {-0.120853 0.356294 1.46872} {0.819809 0.180975 0.376941} {0.646161 0.355458 -0.0897338} {-0.787905 0.0515123 0.319061} {-0.789851 0.142486 0.345597} {-0.85016 0.198843 0.339662} {-0.886174 0.305687 0.274132} {-0.866331 0.354922 0.19283} {-0.812057 0.355358 0.200995} {-0.751663 0.331151 0.151963} {-0.666757 0.313097 -0.00487512} {-0.411777 0.512989 1.30601} {0.808265 0.0565765 0.340094} {0.770586 0.336558 0.130499} {0.553731 0.511381 -0.298035} {0.261964 0.544376 1.39407} {0.125453 1.05768 -1.04469} {-0.760729 0.0144333 0.357592} {-0.892636 0.238414 0.309738} {-0.889139 0.28917 0.223785} {-0.721153 0.291382 0.964049} {-0.712703 0.286947 0.041947} {-0.362828 0.978357 -1.00328} {0.787508 0.339239 0.23598} {0.615247 0.316553 -0.237787} {-0.842963 0.103452 0.328793} {-0.874703 0.142685 0.297333} {-0.869609 0.232702 0.242883} {-0.861966 0.275626 0.162906} {-0.864743 0.327879 0.0959662} {-0.818884 0.368519 0.104426} {-0.763133 0.327492 0.0300152} {-0.704571 0.231073 -0.0499929} {-0.57796 0.53534 -0.628452} {-0.554788 0.750901 -0.899972} {-0.30674 0.395685 1.4096} {0.871582 0.301807 0.26844} {0.887321 0.234043 0.309044} {0.748436 0.284229 0.00502587} {0.699231 0.307207 0.00337397} {0.754497 0.412659 0.923951} {0.563863 0.470034 1.2393} {0.454157 0.749968 -0.877088} {0.253384 0.98646 -0.982149} {-0.711044 -0.318695 0.217146} {-0.867594 0.167506 0.239915} {-0.88377 0.210124 0.160065} {-0.847539 0.272052 0.0570142} {-0.816501 0.341683 0.0109181} {-0.652804 0.209562 -0.102455} {-0.526129 0.454813 1.23484} {-0.276111 1.11003 -1.19921} {0.785946 -0.066407 0.414473} {0.787738 -0.0638239 0.213442} {0.850098 0.109939 0.333013} {0.885731 0.15915 0.32994} {0.913337 0.290623 0.238642} {0.871835 0.343918 0.198659} {0.822053 0.329463 0.000690849} {0.677568 0.441043 1.10361} {0.332571 0.369708 1.42825} {0.385689 0.942871 -1.07662} {0.0722782 0.156072 1.5} {-0.827815 0.0423583 0.284276} {-0.778827 0.00180186 0.264379} {-0.854644 0.155697 0.127921} {-0.873691 0.231521 0.0778896} {-0.833411 0.271911 -0.0395235} {-0.772668 0.264124 -0.0842203} {-0.768466 0.208253 0.804833} {-0.59851 0.29286 -0.431189} {-0.564974 0.31129 1.23712} {-0.565474 0.637456 -0.748237} {-0.548827 0.869715 -1.06692} {-0.30834 0.1978 1.436} {-0.130428 0.0344385 1.48575} {0.877793 0.0921034 0.310401} {0.909869 0.189811 0.28881} {0.894298 0.234218 0.223344} {0.893819 0.281524 0.135918} {0.907404 0.331148 0.158382} {0.731153 0.279534 1.04578} {0.502781 0.615207 -0.74201} {-0.796611 0.0349099 0.199112} {-0.797097 0.0926032 0.221397} {-0.817304 0.123131 0.202016} {-0.834983 0.196944 -0.0475413} {-0.445295 0.339321 1.34248} {0.840159 0.027673 0.251216} {0.761848 0.00141991 0.849077} {0.880216 0.119434 0.272828} {0.872257 0.17311 0.219819} {0.900169 0.227924 0.137833} {0.88306 0.324104 0.040958} {0.86462 0.356331 0.111086} {0.795828 0.260015 -0.103391} {0.560949 0.466293 -0.49675} {0.493649 0.337773 1.34125} {0.470939 0.820592 -1.0306} {0.22294 0.218914 1.48025} {0.163432 1.15402 -1.26145} {-0.756632 -0.0447572 0.171197} {-0.765011 0.0928537 0.144093} {-0.793738 0.115135 0.14} {-0.853683 0.175092 0.0300048} {-0.67777 0.11419 -0.168974} {0.838133 0.0648403 0.238337} {0.8061 0.115572 0.213548} {0.656727 0.242298 -0.128096} {0.359422 1.05945 -1.23192} {-0.75599 0.0312651 0.0965554} {-0.73436 -0.111015 0.832316} {-0.742612 0.15111 -0.177033} {-0.71526 0.013116 1.0056} {-0.619261 0.158884 -0.265185} {-0.497182 0.095765 1.32351} {0.782025 0.0699855 0.190557} {0.826615 0.155056 0.132982} {0.87971 0.199738 0.0594344} {0.896209 0.244954 0.0490177} {0.871704 0.251676 -0.0159862} {0.852564 0.272048 -0.0674275} {0.727058 0.201815 -0.101224} {0.677662 0.150029 -0.148467} {0.62791 0.127166 -0.219742} {0.564961 0.425867 -0.776212} {0.627169 0.289301 1.2246} {0.0199047 -0.184847 1.47276} {-0.752412 0.00816126 0.0290893} {-0.774476 0.133097 0.0770479} {-0.804085 0.125616 -0.0491721} {-0.795643 0.146902 -0.155159} {-0.648323 0.170394 1.14148} {-0.450254 1.03683 -1.21235} {0.770256 -0.351376 0.308086} {0.782398 -0.221737 0.214358} {0.814346 0.019871 0.183276} {0.771802 -0.0545058 0.0923676} {0.785424 0.0376045 0.127616} {0.76376 0.100349 0.136675} {0.770546 0.168333 -0.18302} {0.705944 0.0854852 -0.206011} {0.392114 0.119361 1.41496} {-0.73287 0.0547699 0.0180309} {-0.734569 0.0942478 0.0595291} {-0.604696 -0.113225 1.19463} {-0.672556 -0.0225468 -0.262018} {0.725412 -0.41577 0.69388} {0.776087 0.0183769 0.0574772} {0.764928 0.160201 0.0477551} {0.880985 0.213257 -0.012977} {0.844791 0.183527 -0.0488249} {0.82209 0.175852 -0.155064} {0.591521 0.302565 -0.52356} {0.571617 0.894619 -1.24315} {-0.696439 -0.370884 0.54165} {-0.72021 -0.100767 -0.0242669} {-0.744456 -0.00662616 -0.0474886} {-0.738301 0.0748634 -0.0308957} {-0.758975 0.128261 0.00162734} {-0.788626 0.0655663 -0.150929} {-0.599282 0.96747 -1.26519} {-0.158339 1.20231 -1.33858} {0.664742 -0.646714 0.533359} {0.740615 0.0957493 0.0708294} {0.743801 0.0680221 0.0495542} {0.762825 0.0607799 0.00456301} {0.777389 0.152973 -0.0252138} {0.689993 0.0410725 1.12827} {0.651868 0.0395353 -0.227696} {0.608634 0.0556337 -0.299844} {0.588851 0.13072 -0.429368} {0.544515 0.635248 -0.972247} {0.617513 0.717414 -1.16283} {0.23433 -0.0819541 1.45939} {-0.7892 0.0509336 -0.0867666} {-0.784209 0.0579401 -0.199719} {-0.738172 0.0679979 -0.218027} {-0.582319 0.0627044 -0.467531} {-0.59302 0.211121 -0.665843} {-0.607544 0.384984 -0.70487} {-0.64014 0.592267 -0.987788} {-0.715303 0.851226 -1.26985} {-0.329647 -0.0946013 1.41597} {-0.368351 1.1676 -1.38167} {0.759724 -0.302629 0.0610442} {0.751096 -0.0109937 -0.0553589} {0.772111 0.026504 -0.0166387} {0.718413 0.106192 0.0222396} {0.731962 0.11166 -0.0388291} {0.807494 0.118343 -0.0890815} {0.769389 0.0875201 -0.230937} {0.569349 0.113981 1.29123} {-0.684387 -0.390412 0.806916} {-0.749289 -0.0205917 -0.124624} {-0.763081 -0.00433238 -0.179211} {-0.74764 -0.00288879 -0.218142} {-0.705012 -0.0940226 -0.174203} {-0.622522 0.488583 -0.878396} {-0.647938 0.702496 -1.07375} {-0.75735 0.70242 -1.23519} {-0.163748 -0.251471 1.44534} {0.748155 0.0503707 -0.0847148} {0.80159 0.0817542 -0.180832} {0.708637 0.0130813 -0.240617} {0.616995 -0.0636545 1.22339} {0.576158 -0.0473025 -0.51228} {0.571121 0.184117 -0.702425} {0.591994 0.405978 -0.978194} {0.359804 -0.158164 1.40336} {-0.632521 -0.591232 0.391241} {-0.623713 0.334567 -0.912412} {0.714249 -0.214824 0.972193} {0.72411 -0.0858787 -0.120847} {0.762888 0.00735217 -0.130916} {0.732239 0.049146 -0.0575686} {0.793685 0.051688 -0.111419} {0.755753 0.00900825 -0.220268} {0.671749 -0.0618636 -0.257494} {0.621729 0.546351 -1.08933} {-0.5385 0.0258853 -0.611122} {-0.724141 0.545189 -1.17311} {-0.470179 -0.198315 1.31779} {-0.24746 1.22036 -1.47602} {-0.0687413 -0.426797 1.41018} {0.711882 -0.569957 0.289463} {0.688543 -0.259605 -0.25601} {0.502451 -0.213814 1.30075} {0.0954126 1.23982 -1.46688} {-0.658491 -0.30145 1.03513} {-0.601563 -0.185247 -0.524475} {-0.698062 0.983784 -1.39731} {-0.543713 1.08097 -1.37565} {0.73196 -0.488676 0.0891269} {0.638165 -0.295558 1.12427} {0.697471 0.948413 -1.39807} {0.3379 1.17335 -1.3934} {0.126562 -0.358573 1.42716} {-0.64738 -0.279438 -0.336786} {-0.543401 0.0646688 -0.739077} {-0.661172 0.442652 -1.04503} {-0.318745 -0.410604 1.34919} {-0.834217 0.86015 -1.41733} {-0.543522 1.1028 -1.48121} {0.644263 -0.683288 0.358697} {0.639184 -0.187057 -0.468215} {0.552918 0.0521405 -0.58889} {0.579359 0.284553 -0.888433} {0.725678 0.627483 -1.25547} {0.708627 0.778709 -1.29302} {0.561381 1.07096 -1.42368} {0.418424 1.15072 -1.48935} {-0.558702 -0.682856 0.288032} {-0.640671 -0.55692 0.122986} {-0.636213 -0.572324 0.665976} {-0.685161 -0.344663 -0.0616721} {-0.611914 -0.5563 0.912219} {-0.641342 0.275798 -1.09637} {0.629744 -0.693831 0.199196} {0.675728 -0.444168 0.935293} {0.634274 0.413999 -1.10263} {-0.522272 -0.10848 -0.628368} {-0.590989 0.176142 -0.935012} {-0.520475 -0.429059 1.20095} {-0.800821 0.905555 -1.47759} {0.577849 -0.742368 0.305972} {0.672438 -0.443726 -0.203759} {0.507965 0.0502265 -0.803802} {0.302415 -0.394147 1.36568} {-0.536864 -0.775922 0.462684} {-0.563965 -0.718167 0.574794} {-0.520304 -0.753552 0.77292} {-0.630775 -0.497188 -0.142732} {-0.567141 -0.491432 -0.399192} {-0.466403 -0.0834903 -0.743637} {-0.701003 0.373079 -1.1868} {-0.876192 0.745912 -1.38489} {0.0893447 -0.539517 1.36099} {-0.138802 -0.592663 1.31973} {0.551119 -0.832719 0.429119} {0.639667 -0.648008 -0.0111987} {0.634708 -0.643031 0.820132} {0.49042 -0.0420521 -0.725225} {0.547949 0.143497 -0.922134} {0.589761 0.246465 -1.0348} {0.70017 0.45613 -1.21826} {0.840018 0.779455 -1.40336} {-0.478161 -0.77064 0.342619} {-0.585026 -0.492443 1.07325} {-0.464513 -0.0422436 -0.850678} {0.481636 -0.504374 1.20806} {0.256791 -0.632551 1.26729} {-0.507416 -0.703132 0.247491} {-0.508618 -0.736907 0.133939} {-0.483624 -0.667147 1.05086} {-0.540955 -0.382111 -0.652978} {-0.514209 0.0226036 -0.988511} {-0.777654 0.376255 -1.30865} {-0.834697 0.543148 -1.31604} {-0.352962 -0.617337 1.22207} {0.517517 -0.827763 0.34505} {0.551846 -0.82608 0.566271} {0.602015 -0.746787 0.676414} {0.540391 -0.766315 0.219135} {0.583566 -0.589359 -0.299665} {0.623954 -0.333952 -0.536754} {0.576077 -0.246958 -0.645076} {0.380772 -0.111889 -0.908046} {-0.446806 -0.87508 0.46462} {-0.430552 -0.839979 0.384633} {-0.465067 -0.742399 0.291188} {-0.566576 -0.66034 -0.0416595} {-0.520675 -0.264645 -0.696233} {-0.432781 -0.212993 -0.776665} {-0.575959 0.100921 -1.16987} {-0.917183 0.703942 -1.48329} {0.0516702 -0.736023 1.22576} {-0.180847 -0.732172 1.20259} {0.459899 -0.885696 0.352552} {0.505219 -0.801553 0.301437} {0.576258 -0.577154 1.04625} {0.668966 0.290425 -1.2259} {0.730274 0.943666 -1.5} {-0.410763 -0.840263 0.344818} {-0.479759 -0.832793 0.588998} {-0.667493 0.208526 -1.27136} {0.494059 -0.873112 0.383665} {0.45142 -0.907277 0.414325} {0.498954 -0.183805 -0.714204} {0.440163 -0.00596012 -1.03606} {0.545403 0.125656 -1.11508} {-0.368538 -0.887201 0.409622} {-0.413977 -0.811002 0.317714} {-0.384038 -0.818367 0.282287} {-0.462489 -0.744904 0.224932} {-0.433295 -0.688338 -0.431546} {-0.476657 -0.551186 -0.637406} {-0.916246 0.601059 -1.40724} {0.47028 -0.926153 0.493818} {0.477325 -0.823144 0.251226} {0.429567 -0.918672 0.630444} {0.548562 -0.775322 0.048965} {0.502511 -0.825753 0.799582} {0.53766 -0.413623 -0.714738} {0.816081 0.517838 -1.34177} {0.911484 0.590783 -1.41269} {-0.334874 -0.885644 0.374795} {-0.362498 -0.849638 0.333781} {-0.372386 -0.828847 0.242346} {-0.431647 -0.779731 0.251763} {-0.473602 -0.0410578 -1.27599} {0.389927 -0.92026 0.375952} {0.446582 -0.862285 0.328857} {0.424895 -0.860686 0.290199} {0.463985 -0.822155 0.166347} {0.514536 -0.757972 -0.165196} {0.5161 -0.734129 0.966238} {0.565987 -0.499492 -0.548675} {0.367853 -0.235141 -0.862995} {0.590334 0.123928 -1.27496} {-0.288791 -0.948691 0.450131} {-0.284435 -0.894777 0.40938} {-0.320295 -0.965946 0.517124} {-0.283638 -0.882298 0.368808} {-0.278114 -0.8636 0.346723} {-0.39472 -0.800585 0.179972} {-0.383697 -0.8384 0.902886} {-0.364128 -0.145748 -0.906793} {-0.720061 0.184436 -1.39727} {-0.87527 0.409118 -1.40831} {0.311508 -0.908213 0.375132} {0.347711 -0.928741 0.417545} {0.399375 -0.956466 0.459685} {0.378306 -0.89182 0.345607} {0.423005 -0.854457 0.235328} {0.455344 -0.321105 -0.792418} {0.893192 0.746721 -1.47887} {-0.204884 -0.86016 0.351752} {-0.198385 -0.836559 0.311969} {-0.296752 -0.848775 0.289621} {-0.305761 -0.834223 0.194061} {-0.36538 -0.910707 0.665471} {-0.395966 -0.825017 -0.0410913} {-0.916731 0.486197 -1.4869} {-0.0925074 -0.855793 1.08053} {0.304971 -0.880014 0.345649} {0.325064 -0.998554 0.517145} {0.412876 -0.854804 -0.0248667} {0.501677 -0.679952 -0.370088} {0.48219 -0.638654 -0.564763} {0.478197 0.0201562 -1.22683} {0.718596 0.202544 -1.394} {-0.199545 -0.95332 0.433054} {-0.161618 -0.846765 0.368387} {-0.216798 -0.875248 0.398259} {-0.159358 -0.836296 0.317156} {-0.213721 -0.841336 0.268682} {-0.303214 -0.851332 0.251195} {-0.293916 -0.804205 1.06829} {-0.446091 -0.376871 -0.790524} {-0.300999 -0.345526 -0.890566} {-0.406441 -0.0853526 -1.04917} {-0.581463 0.0520258 -1.34758} {-0.799071 0.260921 -1.4766} {0.185353 -0.873985 0.362684} {0.278215 -0.886433 0.363505} {0.256284 -0.851574 0.323059} {0.322235 -0.984591 0.45979} {0.332399 -0.870461 0.302015} {0.214205 -0.971575 0.718512} {0.37344 -0.854488 0.160791} {0.349555 -0.870608 0.259875} {0.414367 -0.702038 1.12367} {0.85208 0.367567 -1.4711} {0.917183 0.571277 -1.47396} {0.156248 -0.887727 1.01464} {0.250133 -0.779846 1.14325} {-0.12598 -0.963347 0.406192} {-0.135712 -0.89645 0.387526} {-0.17619 -1.00398 0.484169} {-0.112689 -0.847807 0.335138} {-0.165008 -0.826216 0.301142} {-0.235924 -0.841357 0.200511} {-0.276652 -0.859529 -0.303874} {-0.194364 -0.2458 -1.03077} {0.232507 -0.96603 0.429624} {0.252771 -0.883276 0.386537} {0.272728 -0.866041 0.187138} {0.225105 -0.852026 0.292567} {0.361846 -0.834686 0.993353} {0.392389 -0.805583 -0.352822} {0.275397 -0.414086 -0.91459} {0.350849 -0.0936017 -1.18345} {0.55763 0.0381043 -1.38171} {-0.074682 -0.953804 0.353919} {-0.147142 -0.839053 0.245092} {-0.310372 -0.778195 -0.633166} {-0.404496 -0.526302 -0.814087} {-0.283784 -0.159626 -1.1275} {-0.549646 0.00492264 -1.47239} {0.154885 -0.958255 0.384328} {0.118293 -1.00336 0.407897} {0.151337 -0.904257 0.354372} {0.224423 -0.855856 0.339357} {0.197815 -0.868377 0.232487} {0.357629 -0.805404 -0.488648} {0.436206 -0.613907 -0.764789} {0.381208 -0.532836 -0.855708} {-0.0792933 -0.891052 0.328572} {-0.158832 -1.00341 0.580739} {-0.202775 -0.964347 0.71364} {-0.0869271 -0.869335 0.279257} {-0.230336 -0.862317 0.134644} {-0.0333355 -0.945608 0.890091} {0.213988 -1.01119 0.463767} {0.160777 -0.871534 0.310222} {0.278904 -0.912043 -0.0296752} {0.290078 -0.899417 -0.241669} {0.258045 -0.27968 -0.961629} {-0.117454 -0.904415 0.183497} {-0.23016 -0.908066 0.909304} {-0.224537 -0.915631 -0.111651} {-0.287718 -0.829604 -0.467026} {0.122511 -0.952015 0.329014} {0.142703 -0.927373 0.241146} {0.0787786 -1.02393 0.517534} {0.33812 -0.910149 0.832379} {0.189152 -0.959328 0.0483864} {0.362742 -0.757719 -0.698974} {0.397971 -0.0751275 -1.32694} {0.680778 0.137283 -1.47521} {-0.029948 -0.999206 0.394144} {-0.020202 -1.01034 0.29787} {-0.0618331 -0.958097 0.256351} {-0.207323 -0.882382 -0.597802} {-0.264173 -0.640432 -0.901835} {0.127711 -0.995334 0.150196} {0.258426 -0.200956 -1.06232} {-0.186226 -0.926058 0.0349605} {0.0630769 -1.00751 0.326521} {0.0821561 -1.03494 0.239032} {0.010191 -1.10091 0.188895} {-0.100975 -0.984444 0.0956716} {-0.192276 -0.938495 -0.417763} {-0.195162 -0.89969 -0.463669} {-0.232416 -0.870839 -0.516742} {-0.323304 -0.719157 -0.788502} {-0.15263 -0.489128 -0.939803} {-0.136402 -0.213056 -1.25402} {-0.34355 -0.134227 -1.47214} {0.294537 -0.893188 -0.433367} {-0.0554499 -1.09048 0.104973} {-0.0174959 -0.989663 0.683516} {-0.141212 -0.985675 -0.039997} {-0.133303 -0.353638 -0.975793} {0.2745 -0.893335 -0.521476} {0.256298 -0.167307 -1.29048} {0.468136 -0.0490292 -1.47221} {-0.108984 -0.997733 -0.214039} {-0.059584 -1.05339 -0.314144} {-0.0137355 -0.313256 -1.06359} {0.112889 -1.09253 0.0705664} {0.209382 -0.974879 -0.0841045} {0.243054 -0.771945 -0.908194} {-0.158826 -0.968998 -0.165249} {-0.103455 -1.02062 -0.416595} {-0.107437 -0.736823 -0.948027} {0.206253 -0.974343 -0.158726} {0.271199 -0.896008 -0.46328} {0.237059 -0.89969 -0.632882} {0.31707 -0.771482 -0.825561} {0.218701 -0.634898 -0.934229} {0.110581 -0.530363 -0.954061} {0.133906 -0.36092 -0.993691} {0.132253 -0.275977 -1.11178} {0.125904 -0.231196 -1.28016} {-0.122937 -1.04271 -0.166049} {-0.134824 -1.07822 -0.115354} {-0.119483 -0.979835 -0.453232} {-0.197484 -0.869861 -0.773207} {-0.00483738 -0.421231 -0.978107} {0.0421142 -1.17989 0.0644848} {0.194356 -1.04523 -0.115712} {0.136715 -1.01934 -0.242732} {0.191801 -0.981434 -0.449107} {0.20645 -0.981645 -0.508913} {0.00153465 -1.10725 -0.205053} {-0.0584661 -1.04962 -0.156705} {-0.049861 -1.04143 -0.478747} {-0.147495 -0.967177 -0.495382} {-0.0927828 -0.961801 -0.630872} {0.157064 -0.995565 -0.191522} {0.187633 -1.01056 -0.394046} {0.200974 -0.89262 -0.827879} {0.0353866 -0.26656 -1.20378} {-0.0416721 -1.19591 -0.0118616} {-0.0930086 -1.11797 -0.140218} {-0.0295976 -1.07109 -0.404947} {-0.0423313 -1.02811 -0.449496} {-0.190038 -0.186415 -1.47218} {0.0915385 -1.19292 -0.0193322} {0.143769 -1.13643 -0.0748559} {0.169316 -1.0349 -0.170868} {0.096791 -1.03234 -0.178833} {0.090662 -1.06632 -0.292889} {0.0656989 -1.09341 -0.197025} {0.118258 -1.03026 -0.443583} {0.152017 -1.01569 -0.55562} {-0.0314002 -1.15766 -0.161019} {-0.102662 -1.00548 -0.529778} {-0.0269868 -0.580742 -0.954492} {0.0348752 -0.215466 -1.40931} {-0.0580378 -0.207459 -1.45235} {0.098929 -1.05391 -0.147341} {0.139554 -1.11735 -0.144289} {0.121154 -0.972712 -0.645108} {0.152061 -0.194517 -1.4435} {0.295122 -0.146726 -1.46802} {0.0472099 -1.07567 -0.410376} {0.0338514 -1.04295 -0.463385} {-0.0437873 -1.03786 -0.539332} {0.00468699 -0.982834 -0.650106} {-0.164751 -0.842188 -0.897037} {0.0316808 -1.23982 -0.0751715} {0.0932262 -1.2059 -0.117806} {0.131328 -1.04084 -0.489816} {0.0398889 -1.04285 -0.54191} {-0.0510347 -1.2097 -0.116817} {0.0540881 -1.16423 -0.168605} {0.0162999 -1.2209 -0.149119} {0.0760566 -0.761612 -0.957238} {-0.0762573 -0.944471 -0.812137} {0.116634 -0.880288 -0.923409} {-0.0244098 -0.883371 -0.926741} {0.0573688 -0.954362 -0.82283} {-2.57383e-05 1.03217 0.365935}} set lcnx { {1 688 2} {0 688 3} {688 0 4} {1 2 5} {688 1 3} {2 688 8} {1 5 6} {3 9 7} {11 0 7} {3 7 0} {0 11 4} {9 13 7} {8 688 14} {17 9 6} {19 13 9} {10 4 11} {688 4 14} {20 8 14} {20 18 8} {5 15 12} {2 8 16} {9 17 19} {22 11 7} {5 12 6} {6 23 17} {11 21 10} {11 22 21} {6 12 23} {17 24 19} {7 13 22} {28 20 14} {18 33 32} {12 15 27} {32 16 18} {10 35 25} {35 10 21} {18 20 33} {29 13 31} {32 33 38} {23 12 39} {10 34 26} {21 22 36} {20 28 37} {20 37 33} {31 42 29} {12 27 30} {17 23 24} {32 15 16} {10 25 34} {14 26 28} {22 41 36} {19 44 31} {31 46 42} {47 38 33} {23 39 43} {23 43 24} {31 44 46} {40 34 25} {34 40 50} {34 50 26} {21 36 35} {39 30 48} {25 53 40} {28 51 37} {57 27 15} {51 28 26} {33 37 47} {59 47 37} {39 48 55} {39 55 43} {24 43 56} {15 45 57} {41 58 36} {49 64 63} {63 48 49} {30 27 49} {24 56 44} {38 52 45} {54 25 35} {29 42 62} {43 55 65} {43 65 56} {70 57 45} {66 45 52} {74 54 67} {29 62 58} {69 27 57} {70 45 66} {61 53 72} {61 73 40} {54 36 67} {36 68 67} {48 80 55} {82 44 56} {46 44 82} {83 61 72} {50 73 79} {48 63 80} {64 49 77} {55 81 65} {27 69 77} {90 57 70} {46 82 71} {78 72 53} {50 79 84} {92 67 68} {58 85 68} {93 59 51} {59 86 47} {63 96 89} {55 80 81} {69 57 90} {56 98 82} {52 91 66} {51 84 93} {102 86 59} {60 94 76} {63 88 96} {105 96 88} {107 106 88} {56 65 98} {66 99 111} {112 76 104} {85 58 62} {86 102 103} {60 86 94} {95 87 75} {96 105 117} {88 106 105} {118 105 106} {106 107 119} {88 121 107} {120 107 121} {64 97 122} {64 122 88} {109 65 81} {65 109 98} {52 76 91} {75 71 113} {78 74 101} {74 67 101} {85 92 68} {128 62 87} {86 103 94} {129 76 94} {76 129 104} {105 118 117} {132 119 120} {119 107 120} {88 122 121} {77 108 123} {90 70 111} {112 91 76} {61 83 136} {84 79 127} {117 118 138} {89 96 130} {119 132 131} {132 141 140} {132 120 142} {132 142 141} {122 143 121} {81 80 133} {97 123 134} {148 71 124} {150 149 83} {152 73 126} {115 137 79} {92 85 154} {62 128 85} {87 95 128} {156 94 103} {118 139 138} {131 132 139} {139 140 158} {141 142 160} {121 143 120} {122 144 143} {161 143 144} {134 145 144} {83 114 150} {168 150 114} {83 149 136} {136 149 170} {136 170 126} {126 171 151} {72 78 100} {78 101 153} {126 151 152} {128 95 173} {176 138 139} {138 176 116} {177 116 176} {139 132 140} {178 158 159} {141 160 179} {141 179 159} {143 142 120} {160 142 180} {161 144 181} {123 108 162} {146 110 90} {109 184 163} {90 111 185} {186 147 99} {91 112 135} {113 148 187} {114 125 167} {114 167 168} {189 168 167} {150 168 190} {150 190 169} {101 67 92} {101 92 172} {101 172 153} {92 154 172} {102 93 195} {174 156 103} {113 188 175} {176 196 177} {176 139 196} {197 196 139} {139 158 198} {160 180 199} {160 199 179} {161 181 180} {144 145 181} {163 200 124} {99 135 186} {113 187 188} {125 201 189} {167 125 189} {169 190 191} {204 191 190} {169 191 192} {205 192 191} {206 193 192} {126 170 207} {126 207 171} {153 172 194} {79 137 127} {103 102 155} {104 129 213} {177 130 116} {139 198 197} {214 177 196} {196 197 215} {158 178 198} {216 198 178} {217 179 199} {218 145 162} {108 110 183} {200 163 184} {112 164 135} {220 203 219} {190 203 204} {191 204 205} {171 207 206} {115 152 221} {93 127 209} {85 210 154} {85 173 210} {95 212 173} {156 222 129} {95 175 212} {218 225 181} {224 226 182} {226 133 182} {227 218 162} {108 183 227} {108 227 162} {187 200 228} {148 200 187} {203 220 204} {219 229 220} {192 205 232} {233 206 192} {171 206 234} {171 234 208} {153 194 100} {208 235 151} {152 236 221} {237 221 236} {115 221 137} {127 137 209} {174 222 156} {130 177 214} {196 223 214} {178 242 216} {244 180 181} {133 226 245} {109 133 245} {109 245 184} {228 200 184} {111 147 185} {202 247 165} {251 249 250} {201 166 249} {250 249 166} {230 204 220} {220 229 252} {205 231 232} {192 232 233} {206 233 234} {253 254 235} {254 236 235} {236 152 235} {152 151 235} {221 237 137} {102 195 155} {239 154 210} {129 222 213} {188 240 175} {214 223 241} {216 242 215} {256 241 223} {217 199 243} {259 218 227} {135 164 246} {260 247 202} {165 247 248} {166 165 248} {201 249 219} {251 229 249} {220 252 230} {262 231 230} {233 264 234} {233 263 264} {265 208 234} {235 208 253} {209 266 238} {174 211 267} {214 157 130} {215 242 257} {256 223 257} {241 256 270} {271 270 256} {242 272 257} {274 186 246} {250 261 251} {230 252 262} {261 279 278} {261 278 277} {231 262 264} {231 264 263} {100 194 202} {234 264 265} {208 265 253} {281 202 194} {210 173 255} {212 255 173} {287 212 175} {241 270 269} {256 257 271} {272 271 257} {270 271 288} {289 273 244} {225 290 244} {245 226 258} {245 258 228} {185 147 294} {296 187 228} {164 275 297} {252 277 262} {262 280 264} {277 278 301} {302 303 280} {264 280 303} {264 303 265} {253 304 254} {236 254 282} {236 282 237} {194 239 281} {239 210 305} {210 255 305} {212 287 255} {287 175 240} {157 268 89} {270 288 307} {270 307 269} {273 307 288} {290 289 244} {309 290 259} {311 146 185} {312 294 147} {147 186 312} {295 186 274} {261 300 279} {277 301 262} {302 315 303} {303 316 265} {253 265 304} {317 282 254} {319 284 283} {137 284 266} {195 238 285} {289 290 309} {289 309 308} {259 310 309} {146 311 293} {185 294 311} {202 325 260} {299 326 327} {315 302 328} {315 329 303} {329 316 303} {254 304 317} {330 317 304} {317 331 282} {281 239 305} {238 266 320} {285 238 321} {309 310 308} {310 307 308} {227 291 259} {335 228 258} {314 188 296} {326 299 250} {299 327 315} {329 315 327} {326 330 327} {304 316 330} {317 330 331} {222 174 267} {307 310 269} {183 292 291} {293 324 292} {311 324 293} {228 335 296} {295 274 344} {274 246 345} {246 297 345} {275 341 336} {314 337 240} {346 298 247} {299 315 328} {327 330 316} {327 316 329} {331 330 326} {202 281 325} {305 255 340} {213 222 349} {213 349 341} {350 287 240} {351 310 259} {259 291 343} {353 324 311} {353 311 294} {344 355 295} {274 345 344} {275 336 297} {339 331 326} {281 347 325} {358 319 331} {284 359 320} {238 320 360} {347 281 318} {364 341 349} {366 323 157} {367 306 268} {226 342 258} {353 370 324} {312 334 294} {296 335 354} {345 297 356} {297 336 356} {276 357 338} {247 338 346} {372 260 325} {373 332 321} {286 361 362} {222 363 349} {364 349 363} {369 306 367} {259 343 351} {333 374 291} {375 292 324} {294 334 353} {338 357 371} {318 340 347} {286 332 361} {435 364 363} {382 323 365} {323 383 367} {366 157 368} {351 385 368} {343 291 374} {374 333 387} {353 334 388} {353 388 370} {344 377 355} {344 345 356} {314 354 337} {240 337 350} {392 378 357} {394 260 372} {346 379 298} {298 379 339} {319 395 359} {320 380 396} {361 332 398} {350 381 287} {350 390 381} {367 383 384} {367 384 369} {368 385 366} {258 342 376} {351 386 385} {352 387 333} {352 402 387} {292 375 352} {324 370 375} {258 376 335} {354 335 376} {377 344 356} {337 390 350} {357 378 371} {331 339 358} {396 360 320} {340 403 347} {361 399 362} {381 340 322} {435 363 348} {390 404 381} {323 382 383} {369 407 401} {342 401 376} {409 352 375} {354 391 337} {337 391 390} {378 392 413} {392 276 414} {378 416 371} {325 347 372} {358 419 319} {418 419 358} {321 360 397} {332 373 398} {322 287 381} {343 425 408} {425 343 374} {426 374 387} {370 427 375} {334 410 388} {354 376 412} {378 413 432} {394 276 260} {397 360 396} {340 381 403} {381 404 403} {348 399 435} {382 437 383} {407 376 401} {374 426 425} {438 427 370} {438 370 388} {334 411 410} {334 313 411} {391 429 390} {416 378 432} {276 415 414} {393 379 346} {379 393 417} {395 420 380} {443 397 396} {398 373 434} {435 399 489} {400 436 422} {446 445 423} {445 436 423} {436 400 423} {383 437 384} {447 423 405} {447 405 406} {405 365 406} {366 385 424} {449 386 408} {391 412 430} {391 430 429} {432 413 431} {392 439 413} {431 413 439} {392 451 440} {371 416 454} {276 394 415} {347 433 372} {319 441 395} {396 442 443} {397 443 434} {422 444 421} {422 436 444} {459 444 436} {423 447 462} {423 462 446} {385 386 424} {352 409 402} {409 427 463} {410 438 388} {389 428 450} {431 440 464} {432 431 465} {467 416 452} {371 454 393} {394 469 455} {418 379 470} {418 456 419} {395 441 471} {395 471 420} {361 398 457} {399 457 458} {399 458 489} {474 473 444} {477 476 460} {436 445 460} {446 460 445} {446 462 461} {478 461 462} {447 478 462} {406 478 447} {412 376 407} {464 440 484} {464 484 483} {431 464 486} {431 486 465} {453 451 414} {432 465 466} {452 466 487} {452 487 467} {433 394 372} {470 379 417} {418 470 456} {419 456 488} {419 488 441} {488 471 441} {472 434 443} {444 459 474} {476 474 459} {460 446 492} {460 492 477} {493 461 478} {382 421 437} {494 437 421} {424 495 406} {407 369 384} {424 386 448} {449 448 386} {450 496 482} {496 450 428} {497 429 430} {440 485 484} {464 483 486} {498 486 483} {499 485 451} {451 485 440} {465 486 466} {394 433 469} {503 472 443} {505 473 474} {477 492 491} {509 491 492} {461 493 510} {437 494 384} {407 384 479} {424 448 495} {407 511 412} {512 408 425} {426 387 480} {387 402 480} {402 409 514} {485 499 520} {485 520 484} {486 498 521} {486 521 466} {451 453 499} {487 466 524} {487 524 523} {487 523 467} {393 468 417} {468 501 417} {443 442 503} {398 434 457} {457 526 458} {527 489 458} {505 531 530} {473 505 532} {473 532 475} {533 506 508} {491 509 508} {534 508 509} {492 510 509} {535 509 510} {409 463 514} {438 481 515} {438 410 481} {483 539 518} {522 499 453} {524 466 521} {541 540 524} {523 524 540} {433 403 525} {442 545 503} {526 457 434} {529 429 528} {530 531 547} {531 506 533} {421 475 494} {479 384 494} {508 534 548} {508 548 533} {509 535 548} {509 548 534} {510 493 535} {463 427 515} {482 496 516} {429 497 528} {484 520 538} {518 539 556} {518 556 519} {467 523 500} {433 525 469} {543 501 468} {470 502 559} {504 472 546} {583 504 546} {434 504 526} {404 529 525} {404 525 403} {531 533 561} {564 533 548} {565 548 535} {565 535 493} {493 478 565} {408 550 449} {408 512 550} {511 430 412} {430 511 497} {497 566 528} {538 567 554} {517 553 555} {517 555 568} {539 517 556} {519 556 541} {540 569 523} {523 569 500} {468 570 543} {560 456 559} {488 560 544} {544 471 488} {420 471 571} {472 503 546} {529 542 525} {533 564 561} {548 565 572} {574 495 536} {555 553 576} {557 541 568} {577 580 557} {469 542 455} {502 558 581} {503 582 546} {532 584 578} {578 562 532} {561 564 586} {561 586 547} {564 548 572} {479 573 511} {515 552 463} {557 568 577} {589 580 577} {543 558 501} {502 581 559} {544 571 471} {420 571 590} {578 584 554} {547 585 584} {547 586 585} {573 479 494} {591 565 495} {565 478 495} {576 593 577} {577 593 589} {454 500 468} {583 546 610} {584 585 592} {584 592 554} {565 591 572} {495 574 591} {598 597 575} {599 449 550} {513 600 588} {552 602 463} {576 592 593} {570 468 500} {585 594 592} {593 592 594} {585 586 604} {585 604 594} {562 578 605} {586 572 595} {572 591 595} {591 574 606} {575 597 596} {605 578 522} {500 569 570} {543 603 558} {579 542 528} {563 566 573} {595 591 606} {536 575 596} {600 513 607} {513 537 607} {613 607 537} {602 601 551} {569 580 615} {559 616 560} {536 596 612} {618 612 596} {588 599 550} {605 522 566} {603 621 558} {571 544 626} {627 609 590} {545 609 582} {574 617 606} {596 631 618} {596 597 631} {632 587 549} {607 633 600} {593 634 614} {589 614 580} {580 614 635} {580 635 615} {615 620 569} {603 637 621} {558 621 608} {608 622 558} {559 623 616} {616 624 560} {624 544 560} {594 634 593} {606 604 595} {604 606 630} {606 617 630} {617 611 629} {640 629 611} {598 587 642} {607 613 633} {615 635 620} {621 637 638} {646 581 622} {646 623 581} {544 625 626} {627 626 613} {629 640 649} {629 649 630} {650 612 618} {631 642 641} {601 647 613} {614 634 653} {620 635 655} {620 655 644} {636 644 656} {658 657 636} {626 625 633} {633 613 626} {627 613 647} {609 628 669} {640 661 649} {618 651 650} {641 642 662} {601 664 647} {602 652 601} {614 653 654} {667 635 654} {636 656 658} {623 646 616} {628 647 664} {628 664 669} {609 669 670} {639 612 657} {639 657 658} {640 639 661} {673 641 662} {675 632 599} {600 633 663} {634 676 653} {677 654 653} {666 655 667} {655 635 667} {671 645 657} {678 679 660} {679 668 660} {622 668 646} {676 634 648} {639 658 681} {639 681 661} {682 680 661} {645 671 659} {679 672 641} {599 588 675} {683 619 663} {633 625 663} {653 676 677} {654 677 667} {681 666 667} {638 678 660} {616 683 624} {544 624 625} {683 625 624} {648 680 676} {648 630 680} {656 666 658} {666 681 658} {661 681 682} {671 672 659} {678 659 672} {668 679 674} {684 674 643} {675 684 632} {619 675 588} {663 625 683} {667 677 681} {646 685 616} {683 616 685} {681 677 682} {672 679 678} {632 684 643} {675 619 686} {668 674 687} {680 682 676} {682 677 676} {674 684 687} {684 675 686} {619 683 686} {685 646 687} {668 687 646} {526 527 458} {427 409 375} {359 284 319} {600 663 619} {426 480 513} {537 513 480} {97 134 122} {144 122 134} {94 156 129} {629 630 617} {641 672 651} {662 643 673} {566 522 528} {95 75 113} {614 654 635} {472 504 434} {272 243 271} {288 271 243} {149 169 170} {188 314 240} {178 217 242} {470 559 456} {197 198 215} {216 215 198} {163 124 98} {549 599 632} {124 71 82} {318 305 340} {85 128 173} {590 609 545} {53 25 74} {54 74 25} {455 542 579} {239 172 154} {141 159 140} {158 140 159} {304 265 316} {150 169 149} {601 613 537} {148 113 71} {685 687 686} {684 686 687} {285 155 195} {319 419 441} {29 58 41} {8 18 16} {660 668 622} {560 488 456} {73 50 40} {145 218 181} {572 586 564} {170 193 207} {206 207 193} {12 30 39} {368 310 351} {481 552 515} {632 643 587} {391 354 412} {407 479 511} {392 440 439} {431 439 440} {51 59 37} {589 593 614} {61 40 53} {522 578 499} {621 638 608} {641 673 679} {685 686 683} {283 331 319} {545 582 503} {31 13 19} {389 295 355} {48 30 49} {598 642 597} {631 597 642} {558 622 581} {314 296 354} {164 297 246} {181 225 244} {138 116 117} {392 414 451} {320 266 284} {58 68 36} {90 185 146} {231 263 232} {233 232 263} {494 563 573} {452 416 432} {186 135 246} {209 137 266} {562 605 563} {566 563 605} {655 666 644} {656 644 666} {480 402 514} {298 248 247} {98 109 163} {601 537 551} {466 452 432} {554 567 578} {127 93 84} {60 47 86} {495 478 406} {334 312 313} {578 567 499} {229 219 249} {61 136 73} {126 73 136} {133 80 182} {522 579 528} {201 125 166} {82 98 124} {208 151 171} {558 502 501} {475 532 562} {474 507 505} {124 200 148} {390 429 404} {517 538 553} {554 553 538} {225 218 290} {259 290 218} {546 582 610} {113 175 95} {328 302 279} {404 429 529} {139 118 131} {228 184 245} {526 504 583} {76 52 60} {166 125 165} {170 169 193} {192 193 169} {323 366 365} {611 617 536} {267 286 362} {536 612 611} {474 476 507} {490 507 476} {66 111 70} {516 552 481} {299 328 300} {279 300 328} {360 321 238} {623 559 581} {618 631 651} {641 651 631} {66 91 99} {46 75 42} {399 361 457} {483 484 539} {498 519 521} {118 106 131} {119 131 106} {410 411 482} {450 482 411} {323 367 268} {53 74 78} {77 69 108} {188 187 296} {389 313 295} {97 77 123} {529 528 542} {182 89 224} {525 542 469} {427 438 515} {147 111 99} {172 239 194} {100 78 153} {19 24 44} {14 4 26} {505 507 531} {506 531 507} {620 644 570} {636 570 644} {54 35 36} {415 394 455} {557 580 540} {569 540 580} {183 291 227} {49 27 77} {155 285 211} {62 42 87} {75 87 42} {226 224 342} {306 342 224} {495 448 536} {64 77 97} {491 508 490} {93 102 59} {285 321 332} {604 630 648} {638 637 678} {659 678 637} {143 161 142} {180 142 161} {346 338 393} {371 393 338} {286 285 332} {357 276 392} {71 75 46} {421 444 475} {473 475 444} {64 88 63} {650 671 612} {657 612 671} {433 347 403} {561 547 531} {133 109 81} {4 10 26} {507 490 506} {508 506 490} {229 251 252} {289 308 273} {307 273 308} {603 645 637} {659 637 645} {135 99 91} {468 393 454} {305 318 281} {586 595 604} {557 540 541} {601 652 664} {665 664 652} {275 104 341} {157 89 130} {22 13 41} {29 41 13} {291 292 333} {352 333 292} {416 467 454} {500 454 467} {93 209 195} {238 195 209} {410 482 481} {516 481 482} {425 426 512} {513 512 426} {205 204 231} {230 231 204} {280 262 302} {301 302 262} {242 217 272} {243 272 217} {461 510 446} {492 446 510} {104 213 341} {547 584 530} {609 627 628} {647 628 627} {202 165 100} {157 269 368} {310 368 269} {455 579 453} {522 453 579} {619 588 600} {511 573 497} {566 497 573} {555 576 568} {577 568 576} {400 365 423} {405 423 365} {377 428 355} {389 355 428} {536 448 575} {549 575 448} {292 183 293} {323 268 157} {650 651 671} {672 671 651} {73 152 79} {115 79 152} {514 551 480} {537 480 551} {574 536 617} {512 513 550} {588 550 513} {96 117 130} {116 130 117} {519 541 521} {524 521 541} {494 475 563} {562 563 475} {252 251 277} {261 277 251} {180 244 199} {123 162 134} {145 134 162} {306 369 342} {401 342 369} {215 257 196} {223 196 257} {47 60 38} {52 38 60} {295 313 186} {312 186 313} {569 620 570} {396 380 442} {420 442 380} {343 408 351} {386 351 408} {554 592 553} {576 553 592} {642 587 662} {643 662 587} {358 339 418} {379 418 339} {178 159 217} {179 217 159} {449 599 448} {549 448 599} {422 421 400} {420 590 442} {545 442 590} {80 63 182} {89 182 63} {608 638 622} {660 622 638} {463 602 514} {551 514 602} {611 612 640} {639 640 612} {157 214 269} {241 269 214} {630 649 680} {661 680 649} {483 518 498} {582 609 610} {670 610 609} {250 299 261} {300 261 299} {499 567 520} {538 520 567} {518 519 498} {417 501 470} {502 470 501} {459 436 476} {460 476 436} {69 90 108} {110 108 90} {287 322 255} {340 255 322} {604 648 594} {634 594 648} {484 538 539} {517 539 538} {155 211 103} {174 103 211} {104 275 112} {164 112 275} {570 645 543} {603 543 645} {366 424 365} {406 365 424} {89 268 224} {306 224 268} {237 282 283} {331 283 282} {168 189 190} {203 190 189} {189 201 203} {219 203 201} {278 279 301} {302 301 279} {389 450 313} {411 313 450} {83 72 114} {100 114 72} {250 298 326} {339 326 298} {3 1 9} {6 9 1} {146 293 110} {183 110 293} {211 285 267} {286 267 285} {571 626 590} {627 590 626} {359 395 320} {380 320 395} {50 84 26} {51 26 84} {415 455 414} {453 414 455} {636 657 570} {645 570 657} {530 584 505} {532 505 584} {114 100 125} {165 125 100} {476 477 490} {491 490 477} {248 298 166} {250 166 298} {575 549 598} {587 598 549} {243 199 288} {673 643 679} {674 679 643} {199 244 288} {273 288 244} {247 260 338} {276 338 260} {373 321 434} {397 434 321} {2 16 5} {15 5 16} {237 283 137} {284 137 283} {348 267 399} {362 399 267} {382 365 421} {400 421 365} {267 348 222} {363 222 348} {556 517 541} {568 541 517} {32 38 15} {45 15 38}} set obj [list $lvtx $lcnx] } obj_File { # Load data from an OBJ file set fName [tk_getOpenFile -parent . -title "Select OBJ file to load" \ -initialdir "." ] puts "obj file name : $fName" if {[file exists $fName]} { set obj [load_OBJ_File $fName] } else { return } } default { # Load data from a file set fName [tk_getOpenFile -parent . -title "Select a file to load" \ -initialdir "." ] if {[file exists $fName]} { source $fName set obj [list $lvtx $lcnx] } else { return } } } set gvar(display) [list $obj] #################################################################### # Calculate the bounds (xmin,xmax,ymin,ymax,zmin,zmax) # set xmin 9.9999e10 set xmax -9.9999e10 set ymin $xmin set ymax $xmax set zmin $xmin set zmax $xmax foreach obj $gvar(display) { set verts [lindex $obj 0] foreach v $verts { foreach {x y z} $v { set xmin [min $x $xmin] set ymin [min $y $ymin] set zmin [min $z $zmin] set xmax [max $x $xmax] set ymax [max $y $ymax] set zmax [max $z $zmax] } } } set gvar(display,bounds) [list $xmin $xmax $ymin $ymax $zmin $zmax] #################################################################### # Calculate Normal for each face # set newdisp {} foreach obj $gvar(display) { set verts [lindex $obj 0] set conn [lindex $obj 1] set faceNorm {} set newconn {} foreach edge $conn { set i1 [lindex $edge 0] set i2 [lindex $edge 1] set i3 [lindex $edge 2] set v1 [lindex $verts $i1] set v2 [lindex $verts $i2] set v3 [lindex $verts $i3] set u_vector [Get_Vector $v1 $v2] set v_vector [Get_Vector $v1 $v3] set normal [Normal_Vector $u_vector $v_vector] lappend newconn $edge $normal } lappend newdisp [list $verts $newconn] } set gvar(display) $newdisp } # Quick hack at an "OBJ" file parser # # This won't correctly parse an arbitrary OBJ file, but it did # work with the few simple files I tried. With those files # the basic format was: # # v X Y Z <-- "vertex" followed by X,Y,Z coords. # # f v1 v2 v3 ... <-- "face" followed by a list of vertices # # This "hack" only recognizes the above two types and # ignores all other information within the file. # # This should be replaced with a "true" OBJ file parser... # # ...eventually # proc load_OBJ_File {fName} { set lvtx {} set lcnx {} set fp [open $fName r] set data [read $fp] close $fp # Parse vertices from file "v # # #" set RE {^v(.*?)$} set verts [regexp -all -inline -lineanchor -- $RE $data] # Parse faces from file "f # # # ..." set RE {^f(.*?)$} set faces [regexp -all -inline -lineanchor -- $RE $data] foreach {junk v} $verts { if {[string length $v] > 0} { lappend lvtx $v } } foreach {junk f} $faces { if {[string length $f] > 0} { set fnew {} foreach nf $f { incr nf -1 lappend fnew $nf } # lappend lcnx $f lappend lcnx $fnew } } return [list $lvtx $lcnx] } #********************************* Draw_Face ************************ proc Draw_Face { verts edge fNorm {color white} } { global gvar if { [llength $edge] < 3} { return } set nedges [expr {[llength $edge] - 1}] set ilast [lindex $edge $nedges] set vtx [lindex $verts $ilast] foreach {x y z} $vtx {break} d_move $x $y $z 1 for {set n 0} {$n < [llength $edge]} {incr n} { set i [lindex $edge $n] set vtx [lindex $verts $i] foreach {x y z} $vtx {break} d_draw $x $y $z 1 $color } # This is here primarily for debug... # set show_norms 0 if {$show_norms} { set face_cntr [Barycenter3D $verts $edge] foreach {xe ye ze } $gvar(eye_point) {break} foreach {xfc yfc zfc} $face_cntr {break} foreach {x2 y2 z2} $fNorm {break} set xn2 [expr {$xfc + $x2}] set yn2 [expr {$yfc + $y2}] set zn2 [expr {$zfc + $z2}] d_move $xfc $yfc $zfc 1 d_draw $xn2 $yn2 $zn2 1 yellow } } #********************************* Shade_Face ************************ proc Shade_Face { verts edge fNorm {fillcolor blue} {edgecolor white} } { global gvar if { [llength $edge] < 3} { return } set nedges [expr {[llength $edge] - 1}] set ilast [lindex $edge $nedges] set vtx [lindex $verts $ilast] foreach {x y z} $vtx {break} set coordlst [list $x $y $z] for {set n 0} {$n < [llength $edge]} {incr n} { set i [lindex $edge $n] set vtx [lindex $verts $i] foreach {x y z} $vtx {break} lappend coordlst $x $y $z } # draw_shaded $coordlst $fillcolor $edgecolor draw_shaded $coordlst $fillcolor none # This is here primarily for debug... # set show_norms 0 if {$show_norms} { set face_cntr [Barycenter3D $verts $edge] foreach {xe ye ze } $gvar(eye_point) {break} foreach {xfc yfc zfc} $face_cntr {break} foreach {x2 y2 z2} $fNorm {break} set xn2 [expr {$xfc + $x2}] set yn2 [expr {$yfc + $y2}] set zn2 [expr {$zfc + $z2}] d_move $xfc $yfc $zfc 1 d_draw $xn2 $yn2 $zn2 1 red } } proc getFace_color {verts edge fNorm} { global gvar # Get the coords of the center of the face # set face_cntr [Barycenter3D $verts $edge] foreach {xfc yfc zfc} $face_cntr {break} # Get the light coordinates and the normalized # vector from the face center to the light # foreach {xl yl zl } $gvar(light_coord) {break} set dx [expr {$xl - $xfc}] set dy [expr {$yl - $yfc}] set dz [expr {$zl - $zfc}] set mag [expr {$dx * $dx + $dy * $dy + $dz * $dz}] set xn [expr {$dx / sqrt($mag)}] set yn [expr {$dy / sqrt($mag)}] set zn [expr {$dz / sqrt($mag)}] set light_2_face [list $xn $yn $zn] # This is here primarily for debug... # set show_norms 0 if {$show_norms} { d_move $xfc $yfc $zfc 1 d_draw $xl $yl $zl 1 yellow } # Get the intensity of light on the face # returned as a shade of gray # set ival [get_Intensity $fNorm $light_2_face] return $ival } proc get_Intensity {nv l2f} { global gvar set a [Vector_DotProduct $nv $l2f] set b [expr {$a / 2.0 + 0.5}] set ival [max [min $b 1.0] 0.0] set clr [format %2.2x [expr {50 + int(154 * $ival)}]] set colr "\#$clr$clr$clr" return $colr } proc cull_backfaces {verts face } { global gvar set retval {} foreach {edge faceNorm} $face { set i1 [lindex $edge 0] set i2 [lindex $edge 1] set i3 [lindex $edge 2] set v1 [lindex $verts $i1] set v2 [lindex $verts $i2] set v3 [lindex $verts $i3] set u_vector [Get_Vector $v1 $v2] set v_vector [Get_Vector $v1 $v3] set line_of_sight [Get_Vector $v1 $gvar(eye_point)] set normal [Vector_CrossProduct $u_vector $v_vector] ### set mag [Vector_DotProduct $normal $line_of_sight] set mag [Vector_DotProduct $faceNorm $line_of_sight] if {$mag < 0.0} { set visible 0 } else { set visible 1 lappend retval $edge $faceNorm } } return $retval } # sort each face based on it's distance from the "eye_point" # This is to implement the "Painter's Algorithm" hidden surface # removal # proc sortconn { verts face } { global gvar set sconn {} foreach {xe ye ze} $gvar(eye_point) {break} foreach {edge faceNorm} $face { set d 0.0 set nedge [llength $edge] for {set n 0} {$n < $nedge} {incr n} { set i [lindex $edge $n] set vtx [lindex $verts $i] foreach {x y z} $vtx {break} set dx [expr {$x - $xe}] set dy [expr {$y - $ye}] set dz [expr {$z - $ze}] set vdist [expr {sqrt($dx*$dx + $dy*$dy + $dz*$dz)}] set d [expr {$d + $vdist}] } set d [expr {$d / $nedge}] lappend sconn [list $d $edge $faceNorm] } set sconn [lsort -decreasing -index 0 $sconn] set newface {} foreach s $sconn { lappend newface [lindex $s 1] [lindex $s 2] } return $newface } proc canvas_rotate_start {w x y} { global gvar set gvar(canvas,rotate,coords) [list $x $y] } proc canvas_rotate_drag {w x y} { global gvar foreach {newtheta newphi} [canvas_rotate_calc $x $y] {break} dial::setValue $gvar(dtheta) $newtheta dial::setValue $gvar(dazmth) $newphi set gvar(theta) $newtheta set gvar(phi) $newphi set gvar(canvas,rotate,coords) [list $x $y] redraw } proc canvas_rotate_end {w x y} { global gvar foreach {newtheta newphi} [canvas_rotate_calc $x $y] {break} dial::setValue $gvar(dtheta) $newtheta dial::setValue $gvar(dazmth) $newphi set gvar(theta) $newtheta set gvar(phi) $newphi set gvar(canvas,rotate,coords) [list $x $y] redraw } proc canvas_rotate_calc {x y} { global gvar foreach {xold yold} $gvar(canvas,rotate,coords) {break} set dx [expr { ($x - $xold) * 0.01}] set dy [expr {-($y - $yold) * 0.01}] set newtheta [expr {$gvar(theta) - $dx}] set newphi [expr {$gvar(phi) + $dy}] if {$newtheta > $gvar(pi)} { set newtheta [expr {$newtheta - $gvar(twopi)}] } if {$newtheta < $gvar(mpi)} { set newtheta [expr {$newtheta + $gvar(twopi)}] } if {$newphi > $gvar(pi)} { set newphi [expr {$newphi - $gvar(twopi)}] } if {$newphi < $gvar(mpi)} { set newphi [expr {$newphi + $gvar(twopi)}] } set retval [list $newtheta $newphi] return $retval } proc canvas_pan_start {w x y} { global gvar set gvar(canvas,pan,coords) [list $x $y] } proc canvas_pan_drag {w x y} { global gvar foreach {xold yold} $gvar(canvas,pan,coords) {break} set dx [expr { ($x - $xold) * 1.0}] set dy [expr {-($y - $yold) * 1.0}] set gvar(x,translate) [expr {$gvar(x,translate) + $dx}] set gvar(y,translate) [expr {$gvar(y,translate) + $dy}] set gvar(canvas,pan,coords) [list $x $y] redraw } proc canvas_pan_end {w x y} { global gvar foreach {xold yold} $gvar(canvas,pan,coords) {break} set dx [expr { ($x - $xold) * 0.1}] set dy [expr {-($y - $yold) * 0.1}] set gvar(x,translate) [expr {$gvar(x,translate) + $dx}] set gvar(y,translate) [expr {$gvar(y,translate) + $dy}] set gvar(canvas,pan,coords) [list $x $y] redraw } proc canvas_zoom_start {w x y} { global gvar set gvar(canvas,zoom,coords) [list $x $y] } proc canvas_zoom_drag {w x y} { global gvar foreach {xold yold} $gvar(canvas,zoom,coords) {break} set dy [expr {-($y - $yold)}] if {$dy != 0} { set gvar(dist) [expr {$gvar(dist) + $dy * 2}] } set gvar(canvas,zoom,coords) [list $x $y] redraw } proc canvas_zoom_end {w x y} { global gvar foreach {xold yold} $gvar(canvas,zoom,coords) {break} set dy [expr {-($y - $yold)}] if {$dy != 0} { set gvar(dist) [expr {$gvar(dist) + $dy * 2}] } set gvar(canvas,zoom,coords) [list $x $y] redraw } #************************** Draw_Objects ************************* proc Draw_Objects { } { global gvar if {$gvar(hlite)} { set gvar(light_coord) $gvar(eye_point) } foreach obj $gvar(display) { set verts [lindex $obj 0] set face [lindex $obj 1] set conn [lindex $face 0] set faceNorm [lindex $face 1] if {$gvar(backfacecull)} { set face [cull_backfaces $verts $face] } if {$gvar(zsort)} { set face [sortconn $verts $face] foreach {edge fNorm} $face { set colr [getFace_color $verts $edge $fNorm] # set colr blue Shade_Face $verts $edge $fNorm $colr } } else { foreach {edge fNorm} $face { Draw_Face $verts $edge $fNorm } } } if {$gvar(daxis)} { draw_axis $gvar(look_at) } } proc draw_axis { cntr } { global gvar set xtrans $gvar(x,translate) set ytrans $gvar(y,translate) set dist $gvar(dist) set rho $gvar(rho) set gvar(x,translate) [expr {$gvar(Xscreen) * -0.4}] set gvar(y,translate) [expr {$gvar(Yscreen) * -0.4}] set gvar(dist) 500.0 set gvar(rho) 5.0 Set_Viewing_Transform $gvar(rho) $gvar(theta) $gvar(phi) $gvar(dist) foreach {xc yc zc} $cntr {break} set xp [expr {$xc + 0.4}] set yp [expr {$yc + 0.4}] set zp [expr {$zc + 0.4}] d_move $xc $yc $zc 1.0 d_draw $xp $yc $zc 1.0 red last d_move $xc $yc $zc 1.0 d_draw $xc $yp $zc 1.0 green last d_move $xc $yc $zc 1.0 d_draw $xc $yc $zp 1.0 blue last # $gvar(canv) delete axis_text set xp [expr {$xc + 0.5}] set yp [expr {$yc + 0.5}] set zp [expr {$zc + 0.5}] set coords [d_move $xp $yc $zc 1.0] $gvar(canv) create text $coords -text X -justify center -tag axis_text -fill red set coords [d_move $xc $yp $zc 1.0] $gvar(canv) create text $coords -text Y -justify center -tag axis_text -fill green set coords [d_move $xc $yc $zp 1.0] $gvar(canv) create text $coords -text Z -justify center -tag axis_text -fill blue set gvar(x,translate) $xtrans set gvar(y,translate) $ytrans set gvar(dist) $dist set gvar(rho) $rho Set_Viewing_Transform $gvar(rho) $gvar(theta) $gvar(phi) $gvar(dist) } # Get the value of "theta" from the "dial" and update the display # proc get_theta {w} { global gvar set gvar(theta) [dial::getValue $w] redraw } # Get the value of "phi" from the "dial" and update the display # proc get_phi {w} { global gvar set gvar(phi) [dial::getValue $w] redraw } # Get the theta light value from the "dial" and update the display # proc get_theta_L {w} { global gvar set gvar(theta_light) [dial::getValue $w] set sinth [expr {sin($gvar(theta_light))}] set costh [expr {cos($gvar(theta_light))}] set sinph [expr {sin($gvar(phi_light))}] set cosph [expr {cos($gvar(phi_light))}] set x [expr {100. * $sinph * $costh}] set y [expr {100. * $sinph * $sinth}] set z [expr {100. * $cosph}] set gvar(light_coord) [list $x $y $z] redraw } # Get the phi light value from the "dial" and update the display # proc get_phi_L {w} { global gvar set gvar(phi_light) [dial::getValue $w] set sinth [expr {sin($gvar(theta_light))}] set costh [expr {cos($gvar(theta_light))}] set sinph [expr {sin($gvar(phi_light))}] set cosph [expr {cos($gvar(phi_light))}] set x [expr {100. * $sinph * $costh}] set y [expr {100. * $sinph * $sinth}] set z [expr {100. * $cosph}] set gvar(light_coord) [list $x $y $z] redraw } # Update the display # proc redraw {args} { global gvar $gvar(canv) delete all Set_Viewing_Transform $gvar(rho) $gvar(theta) $gvar(phi) $gvar(dist) Draw_Objects } #******************************* D I A L ************************************ # # Quick hack at a simple "dial" widget that handles values # from 0 - 360 degrees. # # To be more generic, this probably needs a lot of work... # namespace eval dial { variable dialInfo set pi [expr {4.0 * atan(1.0)}] set dialInfo(deg_2_rad) [expr {$pi / 180.0}] set dialInfo(rad_2_deg) [expr {180.0 / $pi}] namespace export dial getValue setValue } # Create a dial : The dial displays values in degrees, but uses # an internal value in radians. NOTE: The radian # value is returned to the user when dial::getValue # is called. # # Params : # w : Name of the dial # diameter : diameter of the dial # dlabel : the label used to describe the dial # bg : background color # fg : foreground color # cmd : an optional command to execute when the # dial value changes # proc dial::dial {w diameter dlabel bg fg {cmd ""} } { variable dialInfo set dialInfo($w,name) $w set dialInfo($w,frame) [frame $w -class Dial -relief ridge -bd 2] set width [expr {round($diameter + 10)}] set height [expr {round($diameter + 10)}] set dialInfo($w,canvas) [canvas $w.c -background $bg \ -width $width -height $height \ -closeenough 2.0] set wname $dialInfo($w,canvas) set dialInfo($wname,name) $w set dialInfo($w,cmd) $cmd set dialInfo($w,value) 0.0 set dialInfo($w,value,deg) [expr {$dialInfo($w,value) * $dialInfo(rad_2_deg)}] set dialInfo($w,label) [label $w.label -text $dlabel] set dialInfo($w,lvalue) [label $w.lvalue \ -textvariable dial::dialInfo($w,value,deg)] set rad [expr {$diameter/2}] set xc [expr {$width/2 }] set yc [expr {$height/2 }] set x0 [expr {$xc - $rad}] set y0 [expr {$yc - $rad}] set x1 [expr {$xc + $rad}] set y1 [expr {$yc + $rad}] set coords [list $x0 $y0 $x1 $y1] $w.c create oval $coords -fill $fg set x1 [expr {$xc + $rad * cos($dialInfo($w,value)) }] set y1 [expr {$yc - $rad * sin($dialInfo($w,value)) }] set coords [list $xc $yc $x1 $y1] set dialInfo($w,xc) $xc set dialInfo($w,yc) $yc set dialInfo($w,rad) $rad $w.c create line $coords -fill red -arrow last -tags dial_arrow $w.c bind dial_arrow <ButtonPress-1> {dial::dial_press %W %x %y} $w.c bind dial_arrow <B1-Motion> {dial::dial_motion %W %x %y} $w.c bind dial_arrow <ButtonRelease-1> {dial::dial_motion %W %x %y} pack $w.label -side top pack $w.c -side top pack $w.lvalue -side top return $w } # Mouse button down, start tracking the mouse in order to update the dial # proc dial::dial_press { widget x y } { variable dialInfo set w $dialInfo($widget,name) set xc $dialInfo($w,xc) set yc $dialInfo($w,yc) set dx [expr {$x - $xc}] set dy [expr {$yc - $y}] set theta [expr {atan2($dy,$dx)}] set dialInfo($w,value) $theta set dialInfo($w,value,deg) [format "%6.1f" [expr {$theta * $dialInfo(rad_2_deg)}]] set rad $dialInfo($w,rad) set x1 [expr {$xc + $rad * cos($theta) }] set y1 [expr {$yc - $rad * sin($theta) }] set coords [list $xc $yc $x1 $y1] set canv $dialInfo($w,canvas) $canv coords dial_arrow $coords if {[string length $dialInfo($w,cmd)] > 0} { eval $dialInfo($w,cmd) $w } } # Mouse button motion or up, continue tracking the mouse in order to update the dial # proc dial::dial_motion { widget x y } { variable dialInfo set w $dialInfo($widget,name) set xc $dialInfo($w,xc) set yc $dialInfo($w,yc) set dx [expr {$x - $xc}] set dy [expr {$yc - $y}] set theta [expr {atan2($dy,$dx)}] set dialInfo($w,value) $theta set dialInfo($w,value,deg) [format %6.1f [expr {$theta * $dialInfo(rad_2_deg)}]] set rad $dialInfo($w,rad) set x1 [expr {$xc + $rad * cos($theta) }] set y1 [expr {$yc - $rad * sin($theta) }] set coords [list $xc $yc $x1 $y1] set canv $dialInfo($w,canvas) $canv coords dial_arrow $coords if {[string length $dialInfo($w,cmd)] > 0} { eval $dialInfo($w,cmd) $w } } # The "value" of the dial has changed, so update the arrow on # the canvas # proc dial::update {widget theta} { variable dialInfo set w $dialInfo($widget,name) set xc $dialInfo($w,xc) set yc $dialInfo($w,yc) set rad $dialInfo($w,rad) set dialInfo($w,value) $theta set dialInfo($w,value,deg) [format "%6.1f" [expr {$theta * $dialInfo(rad_2_deg)}]] set x1 [expr {$xc + $rad * cos($theta) }] set y1 [expr {$yc - $rad * sin($theta) }] set coords [list $xc $yc $x1 $y1] set canv $dialInfo($w,canvas) $canv coords dial_arrow $coords } # Get the current value of the dial (in RADIANS) # proc dial::getValue {widget} { variable dialInfo set w $dialInfo($widget,name) return $dialInfo($w,value) } # Set the current value of the dial (in RADIANS) # proc dial::setValue {widget val} { variable dialInfo set w $dialInfo($widget,name) set dialInfo($w,value) $val set dialInfo($w,value,deg) [expr {$val * $dialInfo(rad_2_deg)}] dial::update $widget $dialInfo($w,value) } #******************************* M A I N ************************************ proc main { } { global gvar Init puts "obj : [lindex $gvar(obj_list) 0]" ReadData [lindex $gvar(obj_list) 0] Set_Viewing_Transform $gvar(rho) $gvar(theta) $gvar(phi) $gvar(dist) Draw_Objects } main
AMG: I wrote a [canvas]-based 3D viewer for school this past semester. Have a look; it's at Lab 3D. It's a little clumsy to use because it's constrainted by the assignment, but we can (and should) fix that over time.
MBS 12-20-2005 : I added a 'quick and dirty' OBJ file reader. It only processes the following two types of commands:
v X Y Z <-- "vertex" followed by X,Y,Z coords. f v1 v2 v3 ... <-- "face" followed by a list of vertices
all other lines within the file are ignored. It appears that this simple parser can also handle the data files that AMG refers to within Lab 3D ( [L4 ] )
MBS 12-20-2005 : Just some thoughts about future enhancements :
Any other suggestions ?
HJG In the above program are references to shuttle.dat and torus.dat, where can these files be found ?
MBS The data in those files was taken directly from the data found in 3D polyhedra with simple tk canvas. I've included those files, plus a few others in [L5 ].