#!/bin/sh
# the next line restarts using vtk \
    exec /usr/local/vtk2.2/bin/vtk -f "$0" "$@"
#
# shview3d.tcl: View SHADOW 3D Layout.
#
# ------------------------------------------------
#   Mumit Khan <khan@NanoTech.Wisc.EDU>
#     Center for NanoTechnology
#   University of Wisconsin-Madison
# 1415 Engineering Drive, Madison, WI 53706
# ------------------------------------------------
#
# copyright (c) 2000 Mumit Khan
#

######################### Initialize options ##############################


#
# Preload the any procs if you don't have tclIndex HERE.
# Note: this shouldn't be necessary, since auto_load should take care of 
# it.
#

### INITIALIZATION MODULE

######################### Initialize globals ##############################

### Globals used via the ``gvars'' array. Most of these initialized 
### at startup from ``gdefaults'' (initialize_defaults) in initialize_globals.
#
####
#### User settable parameters.
####
#
##
## GUI state variables.
##
# background_color ................. color: Display background
# axes_color ......................  color: Co-ordinate axes
# optax_color ...................... color: Optical Axis actor
# ray_color ........................ color: Ray actor
# mirror_color ..................... color: Mirror actor
#
# axes_opacity ....................  opacity: Co-ordinate axes
# optax_opacity .................... opacity: Optical Axis actor
# ray_opacity ...................... opacity: Ray actor
# mirror_opacity ................... opacity: Mirror actor
#
# view ............................. enum: Left/Right/Top/Bottom/etc
#
##
## Display control parameters.
##
## Generic.
# axes_visible ..................... boolean: Show/Hide Coord axes
## For Raw reader.
# optax_visible .................... boolean: Show/Hide optax
# rays_visible ..................... boolean: Show/Hide rays
# mirrors_visible .................. boolean: Show/Hide mirrors
## For VTK reader.
# vtk_visible ...................... boolean: Show/Hide
#
# scale_cartesian .................. boolean: Scale data to cartesian
#
##
## Misc. runtime parameters/state variables.
##
# verbose .......................... boolean: turn on/off verbose mode
# file_loaded ...................... boolean: file loaded or not
# file_type ........................ enum: vtk/raw (N/A if !file_loaded)
#
####
#### Fixed parameters.
####
#
##
## VTK variables.
##
# ren .............................. VTK renderer
# renWin ........................... VTK rendering window
# reader ........................... VTK or RAW reader
# vtk_actors_raw ................... List of VTK actors in the renderer
# vtk_actors_vtk ................... List of VTK actors in the renderer
#
##
## GUI objects/paths/windows.
##
# render_w ......................... path: rendering window 
# menubar .......................... path: top menubar
# status_w ......................... path: bottom status sbar
#
###########################################################################

#### 
#
# The VTK internal Tcl files wrapped for transport are at the end of
# this file to avoid clutter. The call to run_main is right after that
# to get the ball rolling. For more information about these files and
# their origin, see the comments at the start of the block marked by 
# "START/END VTK Internal Tcl Files" towards the end of this file.
#
# VTK versions 4.0+ now come with Tcl packages, which obviates the need 
# for such hacks. FIXME/TODO: Obsolete the color proc as well.
#
# colors.tcl       -> initialize_vtk_colors
# vtkInt.tcl       -> initialize_vtk_internal
# TkInteractor.tcl -> initialize_vtk_interactor
# WidgetObject.tcl -> initialize_vtk_widget
#
###

proc initialize_environment {} {
  global env gvars

  # Check for correct Tcl version
  if {[info tclversion] < 8.0} {
    puts stderr "shview3d: Tcl version < 8.0 cannot be used with shview3d"
    exit 1
  }

  # Handle environment variables
  if [catch {set env(SHADOW_ROOT)} SHADOW_ROOT] {
    set env(SHADOW_ROOT) /usr/local/shadow
  }
  if [catch {set env(SHADOW_TCL_LIBRARY)} SHADOW_TCL_LIBRARY] {
    set env(SHADOW_TCL_LIBRARY) $env(SHADOW_ROOT)/lib
  }

  # Set-up various paths
  lappend auto_path . $env(SHADOW_TCL_LIBRARY)/shview3d
}

proc initialize_modules {} {
  global env gvars

  # Running VTK 4.0 or 3.x?
  if {[catch {package require vtkbase}]} {
    # On Windows, it's a loadable extension.
    catch {load vtktcl}
  } else {
    # Have VTK 4.0 or newer.
    if {[catch {package require vtkgraphics}] || \
        [catch {package require vtkrendering}] || \
        [catch {package require vtkpatented}] || \
        [catch {package require vtkio}] || \
        [catch {package require vtkhybrid}] || \
        [catch {package require vtkinteraction}]} {
      tk_dialog .shview3d {Initialization error} \
	"Shview3d requires Visualization Toolkit to be installed ($msg1\n$msg2).  Aborting." \
	"error" 0 Dismiss
      exit 1
    }
  }

  vtkVersion vtk_version_obj
  set gvars(vtk_version) "[vtk_version_obj GetVTKVersion]"
  set gvars(vtk_major_version) "[vtk_version_obj GetVTKMajorVersion]"
  set gvars(vtk_minor_version) "[vtk_version_obj GetVTKMinorVersion]"
  set gvars(vtk_build_version) "[vtk_version_obj GetVTKBuildVersion]"
  vtk_version_obj Delete

  if {[info command vtkRenderer] == ""} { 
    tk_dialog .shview3d {Initialization error} \
      "Shview3d requires Visualization Toolkit to be installed.  Aborting." \
      "error" 0 Dismiss
    exit 1
  }
  #
  # To avoid carrying around VTK internal Tcl files for Tk interaction
  # and so on, we wrap those files and initialize those here. VTK versions
  # 4.0+ now come with Tcl packages, which obviates the need for such 
  # hacks. FIXME/TODO: Obsolete the color proc as well.
  #
  initialize_vtk_colors
  if {[expr {$gvars(vtk_major_version) < 4}]} {
    initialize_vtk_interactor
  }
}

proc initialize_defaults {} {
  global gvars gdefaults
  #
  # make all the globals available here.
  #
  eval global [uplevel \#0 info vars]

  ###
  ### Signaling/private globals are kept in toolsetPriv.
  ###

  ### Defaults. Change these only, and these will propagate throughout
  ### the program.
  ###
  set gdefaults(verbose)			0
  ##
  # some colors for good contrast
  set gdefaults(background_color)	"[expr 89/255.0] 1.0 1.0"
  set gdefaults(axes_color)		$black
  set gdefaults(optax_color)		$red
  set gdefaults(ray_color)		"0.3800 0.7000 0.1600"
  set gdefaults(mirror_color)		"1.0 0.713725490196 0.635294117647"
  ##
  # all opaque
  set gdefaults(axes_opacity)		1.0
  set gdefaults(optax_opacity)		1.0
  set gdefaults(ray_opacity)		1.0
  set gdefaults(mirror_opacity)		1.0
  ##
  # Visibility defaults
  set gdefaults(axes_visible)		1
  set gdefaults(optax_visible)		1
  set gdefaults(ray_visible)		1
  set gdefaults(mirror_visible)		1
  set gdefaults(vtk_visible)		1
  ##
  # Scaling issues.
  set gdefaults(scale_cartesian)	1
  #
  set gdefaults(view)			Left
  ###
}

proc initialize_globals {} {
  global gvars gdefaults
  array set gvars [array get gdefaults]
  set gvars(file_loaded) 0

  # The following globals are set in data reader/creator routines.
  # global g_data g_optax g_rays g_mirrors
}

proc initialize_gui {} {
  global gvars

  wm title . "SHADOW Layout Visualizer"
  wm protocol . WM_SAVE_YOURSELF "menu_cmd:exit"
  wm protocol . WM_DELETE_WINDOW "menu_cmd:exit"

  set gvars(menubar) [make_topmenu .mbar]
  . configure -menu $gvars(menubar)

  set gvars(render_w) .render_w
  vtkTkRenderWidget $gvars(render_w) -width 600 -height 600
    BindTkRenderWidget $gvars(render_w)
  wm colormapwindows . $gvars(render_w)
  pack $gvars(render_w) -side top -anchor nw -padx 3 -pady 3 \
    -fill both -expand 1

  # Status bar
  frame .bottomF -relief sunken -borderwidth 3
  label .bottomF.status -borderwidth 0
  pack .bottomF.status -side top -anchor w -expand 1 -fill x -padx 0 -pady 0
  pack .bottomF  -side top -anchor w -expand 1 -fill x -padx 0 -pady 0

  set gvars(status_w) .bottomF.status

  focus $gvars(menubar)
}

proc initialize_vtk {} {
  make_pipeline
  make_banner
}

proc initialize_renderer {} {
  global gvars 

  set gvars(renWin) [$gvars(render_w) GetRenderWindow]
  $gvars(renWin) SetSize 600 600

  set gvars(ren) [vtkRenderer ren1]
  $gvars(renWin) AddRenderer $gvars(ren)

  $gvars(ren) AddActor bannerActor
  [$gvars(ren) GetActiveCamera] Zoom 1.25
}

proc initialize {} {
  initialize_environment
  initialize_modules
  initialize_defaults
  initialize_globals
  initialize_gui
  initialize_vtk
  initialize_renderer
}

#----------------------------------------------------------------------#
# Make the top pulldown menu bar
#----------------------------------------------------------------------#

proc make_topmenu {menu_name} {
  global gvars

  # make menu bar itself
  menu $menu_name -tearoff 0

  #
  # add the top level menu buttons.
  # 
  # The toplevel looks like:
  #
  #    FILE VIEW OPTIONS	HELP
  #

  $menu_name add cascade \
    -label File \
    -menu $menu_name.file \
    -underline 0

  menu $menu_name.file -tearoff 0

  $menu_name add cascade \
    -label View \
    -menu $menu_name.view \
    -underline 0

  menu $menu_name.view -tearoff 0

  $menu_name add cascade \
    -label Options \
    -menu $menu_name.options \
    -underline 0

  menu $menu_name.options -tearoff 0

  $menu_name add cascade \
    -label Help \
    -menu $menu_name.help \
    -underline 0

  menu $menu_name.help -tearoff 0

  ### FILE 
  ##    -> Open
  ##    -> Export
  ##         -> EPS
  ##         -> JPEG
  ##         -> TIFF
  ##         -> PPM
  ##         -> VRML
  ##         -> VTK
  ##    -> -----
  ##    -> Exit
  ###

  $menu_name.file add command \
    -command {menu_cmd:open_file} \
    -label {Open File ...} \
    -accelerator {Ctrl+O} \
    -underline {0}

  bind all <Control-o>	"menu_cmd:open_file"

  $menu_name.file add cascade \
    -label Export \
    -menu $menu_name.file.export \
    -underline {0}

  menu $menu_name.file.export -tearoff 0

  foreach format {EPS JPEG TIFF PPM VTK VRML} {
    $menu_name.file.export add command \
      -command "menu_cmd:export $format" \
      -label "$format format ..." \
      -underline {-1}
  }

  $menu_name.file add separator

  $menu_name.file add command \
    -command {menu_cmd:exit} \
    -label {Exit} \
    -underline {1} \
    -accelerator {Ctrl+X}

  bind all <Control-x>	"menu_cmd:exit"

  ### VIEW
  ##    -> Visibility
  ##         -> Optical Axis
  ##         -> Rays
  ##         -> Mirrors
  ##         -> Coordinate Axes
  ##    -> Camera
  ##         -> Front
  ##         -> Optax
  ##         -> Back
  ##         -> Left
  ##         -> Right
  ##         -> Top
  ##         -> Bottom
  ##         -> Isometric
  ##    --- SEPARATOR ---
  ##         -> Save View
  ##         -> Restore View
  ###

  $menu_name.view add cascade \
    -label Visibility \
    -menu $menu_name.view.visibility \
    -underline 0

  menu $menu_name.view.visibility -tearoff 0

  catch {unset names}
  set names(optax)	"Optical Axis" 
  set names(ray)	"Rays" 
  set names(mirror)	"Mirrors" 
  set names(axes)	"Coordinate Axes" 

  foreach what "optax ray mirror axes" {
    $menu_name.view.visibility add checkbutton \
      -label $names($what) \
      -variable gvars(${what}_visible) \
      -command "menu_cmd:change_visibility $what" \
      -underline {0}
  }
 
  $menu_name.view add cascade \
    -label Camera \
    -menu $menu_name.view.camera \
    -underline 0

  menu $menu_name.view.camera -tearoff 0

  set views(Front)	" 1  0  0  0  1  0"
  set views(Back)	"-1  0  0  0  1  0"
  set views(Left)	" 0  0  1  0  1  0"
  set views(Right)	" 0  0 -1  0  1  0"
  set views(Top)	" 0  1  0  0  0  1"
  set views(Bottom)	" 0 -1  0  0  0  1"
  set views(Isometric)	" 1  1  1  0  1  0"

  foreach view {
    {Front 0} {Back 0} {Left 0} {Right 0} {Top 0} {Bottom 1} {Isometric 0}
  } {
    set view_name [lindex $view 0]
    set underline [lindex $view 1]
    set view_cmd "eval menu_cmd:update_view [set views(${view_name})]"
    $menu_name.view.camera add radiobutton \
      -label $view_name \
      -variable gvars(view) \
      -command "$view_cmd" \
      -underline $underline
  }

  $menu_name.view.camera add radiobutton \
    -label {Default} \
    -variable gvars(view) \
    -command "menu_cmd:default_view" \
    -underline 0
  
  $menu_name.view.camera add separator
  $menu_name.view.camera add command \
    -label {Save to clipboard} \
    -command "menu_cmd:save_view_to_clipboard" \
    -underline -1
  
  $menu_name.view.camera add command \
    -label {Restore from clipboard} \
    -command "menu_cmd:restore_view_from_clipboard" \
    -underline -1

  $menu_name.view.camera add command \
    -label {Clear view clipboard} \
    -command "global gsaved_view; catch {unset gsaved_view}" \
    -underline -1

  $menu_name.view.camera add separator
  $menu_name.view.camera add command \
    -label {Save to file ...} \
    -command "menu_cmd:save_view" \
    -underline -1
  
  $menu_name.view.camera add command \
    -label {Restore from file ...} \
    -command "menu_cmd:restore_view" \
    -underline -1
  
  
  ### OPTIONS
  ##    -> Colors
  ##         -> Optax
  ##         -> Rays
  ##         -> Mirrors
  ##         -> Axes
  ##         -> Background
  ##    -> Opacity
  ##         -> Optax
  ##         -> Rays
  ##         -> Mirrors
  ##         -> Axes
  ##    -> Cartesian Scaling
  ##    -> Show Data
  ##    -> View Dimensions
  ##    SEPARATOR
  ##    -> Verbose Mode (Debugging)
  ###

  ####################

  $menu_name.options add cascade \
    -label Colors \
    -menu $menu_name.options.colors \
    -underline 0

  menu $menu_name.options.colors -tearoff 0

  catch {unset names}
  set names(optax)	"Optical Axis" 
  set names(ray)	"Rays" 
  set names(mirror)	"Mirrors" 
  set names(axes)	"Coordinate Axes" 
  set names(background)	"Background" 

  foreach what "optax ray mirror axes background" {
    $menu_name.options.colors add command \
      -label $names($what) \
      -command "menu_cmd:change_color $what" \
      -underline {0}
  }
 
  ####################

  $menu_name.options add cascade \
    -label Opacity \
    -menu $menu_name.options.opacity \
    -underline 0

  menu $menu_name.options.opacity -tearoff 0

  catch {unset names}
  set names(optax)	"Optical Axis" 
  set names(ray)	"Rays" 
  set names(mirror)	"Mirrors" 
  set names(axes)	"Coordinate Axes" 

  foreach what "optax ray mirror axes" {
    $menu_name.options.opacity add command \
      -label $names($what) \
      -command "menu_cmd:change_opacity $what" \
      -underline {0}
  }
 
  ####################

  $menu_name.options add checkbutton \
    -label {Cartesian Scaling} \
    -variable gvars(scale_cartesian) \
    -command menu_cmd:change_scaling \
    -underline {10}

  $menu_name.options add command \
    -label {Show Data} \
    -command menu_cmd:show_data \
    -underline {5}

  if {0} {
  $menu_name.options add command \
    -label {View Dimensions} \
    -command menu_cmd:view_dimensions \
    -underline {0}
  }

  $menu_name.options add separator
  
  $menu_name.options add checkbutton \
    -label {Verbose Mode (Debugging)} \
    -variable gvars(verbose) \
    -underline {0}

  ### HELP
  ##    -> About
  ##    -> Author
  ###

  $menu_name.help add command \
    -label {About} \
    -command {about_cmd} \
    -state disabled \
    -underline {0}

  $menu_name.help add command \
    -label {Author} \
    -command {about_author_cmd} \
    -state disabled \
    -underline {1}

  return $menu_name
}


###
### VTK initialization. Create pipeline, renderer and rendering window.
###


#
# make_pipeline: Create the VTK pipeline. Since we support two somewhat 
# different input formats, "raw" and "vtk" (see load_file), we end up 
# with two different pipelines, and choose the right actors when we 
# display the scene.
#
# "raw" format Pipeline: This has 3 different data sources, optax, rays
# and the mirrors, so has 3 parallel, but identical, pipelines:
#
#    vtkPolyData -> vtkPolyDataMapper -> vtkActor
#        ^
#    vtkCellArray < vtkPoints < [Layout Tool "raw" format output]
#
# "vtk" format Pipeline:
#
#     vtkPolyDataMapper -> vtkActor
#            ^
#    vtkPolyDataReader < [Layout Tool "vtk" format output]
#
#
# Both formats include the common Coordinate Axes and Axes label actors.
#

proc make_pipeline {} {
  global gvars 

  #
  # Create the Coord Axes and Axes label actors that are common to both
  # pipelines.
  #
  # Assembling the actors representing the Axes and the 3 labels sounds 
  # like a good idea, but the problem is that we want to use vtkFollower
  # to render the labels, and that won't work in the assembly. Also the
  # relative placement doesn't work as simply as it's done now.
  #
  vtkAxes axes
    axes SetSymmetric 0
  vtkPolyDataMapper axes_map
    axes_map SetInput [axes GetOutput]
  vtkActor axes_actor
    axes_actor SetMapper axes_map
    eval [axes_actor GetProperty] SetColor $gvars(axes_color)
    eval [axes_actor GetProperty] SetOpacity $gvars(axes_opacity)
  
  # Now the axes labels.
  foreach axis {X Y Z} {
    vtkVectorText ${axis}_axis_text
    ${axis}_axis_text SetText "$axis"
    vtkPolyDataMapper ${axis}_axis_map
    ${axis}_axis_map SetInput [${axis}_axis_text GetOutput]
    vtkFollower ${axis}_axis_actor
    ${axis}_axis_actor SetMapper ${axis}_axis_map
    eval ${axis}_axis_actor SetOrigin [${axis}_axis_map GetCenter]
    eval [${axis}_axis_actor GetProperty] SetColor $gvars(axes_color)
    eval [${axis}_axis_actor GetProperty] SetOpacity $gvars(axes_opacity)
  }

  # The data sources are initially empty, and is populated 
  # after the data file is loaded (see populate_data_sources, load_file).

  # Create pipeline for raw data.
  set gvars(vtk_actors_raw) [list]

  vtkPoints ray_points
  vtkPoints optax_points
  vtkPoints mirror_points

  vtkCellArray optax_lines
  vtkCellArray ray_lines
  vtkCellArray mirror_lines

  vtkPolyData ray_curve
    ray_curve SetPoints ray_points
    ray_curve SetLines ray_lines

  vtkPolyDataMapper ray_map
    ray_map SetInput ray_curve

  vtkActor ray_actor
    ray_actor SetMapper ray_map
    eval [ray_actor GetProperty] SetColor $gvars(ray_color)
    [ray_actor GetProperty] BackfaceCullingOn
    [ray_actor GetProperty] SetOpacity $gvars(ray_opacity)

  lappend gvars(vtk_actors_raw) ray_actor
  vtkPolyData optax_curve
    optax_curve SetPoints optax_points
    optax_curve SetLines optax_lines

  vtkPolyDataMapper optax_map
    optax_map SetInput optax_curve

  vtkActor optax_actor
    optax_actor SetMapper optax_map
    eval [optax_actor GetProperty] SetColor $gvars(optax_color)
    [optax_actor GetProperty] BackfaceCullingOn
    [optax_actor GetProperty] SetOpacity $gvars(optax_opacity)

  lappend gvars(vtk_actors_raw) optax_actor

  vtkPolyData mirror_curve
    mirror_curve SetPoints mirror_points
    mirror_curve SetPolys mirror_lines

  vtkPolyDataMapper mirror_map
    mirror_map SetInput mirror_curve

  vtkActor mirror_actor
    mirror_actor SetMapper mirror_map
    eval [mirror_actor GetProperty] SetColor $gvars(mirror_color)
    [mirror_actor GetProperty] SetOpacity 1.0
    [mirror_actor GetProperty] SetOpacity $gvars(mirror_opacity)
  
  lappend gvars(vtk_actors_raw) mirror_actor

  # Create pipeline for VTK data.

  set gvars(vtk_actors_vtk) [list]
  vtkPolyDataReader vtk_reader
  vtkPolyDataMapper vtk_map 
    vtk_map SetInput [vtk_reader GetOutput]
  vtkActor vtk_actor
    vtk_actor SetMapper vtk_map
  lappend gvars(vtk_actors_vtk) vtk_actor
}

proc make_banner {} {
  vtkVectorText banner
    banner SetText "             SHADOW\nLayout Visualization Tool"
  vtkPolyDataMapper bannerMapper
    bannerMapper SetInput [banner GetOutput]
  vtkActor bannerActor
    bannerActor SetMapper bannerMapper
  set property [bannerActor GetProperty]
  eval $property SetColor 0.8900 0.8100 0.3400
}

### I/O MODULE

###
### Work horse routines.
###

#
# read_one_line: Read a single non-whitespace line from input, and 
# optionally ignore comments. 
#
proc read_one_line {fin {ignore_comment 1}} {
  while {[gets $fin line] >= 0} {
    # is it a comment or empty line?
    if {([regexp {^[ \t]*#} $line] == 0 || !$ignore_comment) &&
	[regexp {^[ \t]*$} $line] == 0} {
      break
    }
  }
  return $line
}

#
# Read the raw 3D layout data.
#
proc read_data {file data_map optax_map rays_map mirrors_map} {
  upvar $data_map data
  upvar $optax_map optax
  upvar $rays_map rays
  upvar $mirrors_map mirrors
  
  if {[catch {open $file "r"} fid]} {
    error "Cannot open file $file ($fid)"
  }

  # First line should have the magic comment, so check it.
  # Magic comment "# SHADOW Layout version <x>.<y>"
  set line [read_one_line $fid 0]
  if {[lsearch $line "SHADOW"] == -1} {
    error "Illegal SHADOW Layout raw data format. Can't find magic header." \
  }

  set line [read_one_line $fid 0]
  foreach {num_rays num_mirrors} $line { }
  set line [read_one_line $fid]
  foreach {xmin ymin zmin xmax ymax zmax} $line { }

  set data(num_rays) $num_rays
  set data(num_mirrors) $num_mirrors
  set data(xmin) $xmin
  set data(ymin) $ymin
  set data(zmin) $zmin
  set data(xmax) $xmax
  set data(ymax) $ymax
  set data(zmax) $zmax

  # Optical axis.
  set line [read_one_line $fid]
  set np [lindex $line 0]
  set offset 1
  set optax(np) $np
  for {set pt 1} {$pt <= $np} {incr pt} {
    set x [lindex $line $offset]
    incr offset
    set y [lindex $line $offset]
    incr offset
    set z [lindex $line $offset]
    incr offset

    set optax($pt,x) $x
    set optax($pt,y) $y
    set optax($pt,z) $z
  }

  # Rays.

  for {set ray 1} {$ray <= $num_rays} {incr ray} {
    set line [read_one_line $fid]
    set np [lindex $line 0]
    set offset 1
    set rays($ray,np) $np
    for {set pt 1} {$pt <= $np} {incr pt} {
      set x [lindex $line $offset]
      incr offset
      set y [lindex $line $offset]
      incr offset
      set z [lindex $line $offset]
      incr offset

      set rays($ray,$pt,x) $x
      set rays($ray,$pt,y) $y
      set rays($ray,$pt,z) $z
    }
  }

  for {set mirror 1} {$mirror <= $num_mirrors} {incr mirror} {
    for {set face 1} {$face <= 6} {incr face} {
      set line [read_one_line $fid]
      set np [lindex $line 0]
      set offset 1
      set mirrors($mirror,$face,np) $np
      for {set pt 1} {$pt <= $np} {incr pt} {
	set x [lindex $line $offset]
	incr offset
	set y [lindex $line $offset]
	incr offset
	set z [lindex $line $offset]
	incr offset

	set mirrors($mirror,$face,$pt,x) $x
	set mirrors($mirror,$face,$pt,y) $y
	set mirrors($mirror,$face,$pt,z) $z
      }
    }
  }

  close $fid
}

#
# Get the scale factors using the Y axis as the base (typically the
# longest for SHADOW created x-ray optical systems).
#
proc get_scale_factors {data_map} {
  upvar $data_map data

  set x_w [expr {$data(xmax) - $data(xmin)}]
  set y_w [expr {$data(ymax) - $data(ymin)}]
  set z_w [expr {$data(zmax) - $data(zmin)}]

  set yscale 1.0
  set zscale [expr {$y_w / $z_w}]
  set xscale [expr {$y_w / $x_w}]

  return [list $xscale $yscale $zscale]
}

#
# Scale the 3D data to fit.
#
# CHECK/UNUSED: NOT USED CURRENTLY.
#
proc scale_data {data_map optax_map rays_map mirrors_map} {
  upvar $data_map data
  upvar $optax_map optax
  upvar $rays_map rays
  upvar $mirrors_map mirrors

  set num_rays $data(num_rays)
  set num_mirrors $data(num_mirrors)

  set xmin $data(xmin)
  set ymin $data(ymin)
  set zmin $data(zmin)
  set xmax $data(xmax)
  set ymax $data(ymax)
  set zmax $data(zmax)

  set x_w [expr {$xmax - $xmin}]
  set y_w [expr {$ymax - $ymin}]
  set z_w [expr {$zmax - $zmin}]

  # This is the scale factor.
  set max_w $x_w

  if {$max_w < $y_w} { set max_w $y_w }
  if {$max_w < $z_w} { set max_w $z_w }

  for {set ray 1} {$ray <= $num_rays} {incr ray} {
    for {set pt 1} {$pt <= $rays($ray,np)} {incr pt} {
      set x $rays($ray,$pt,x)
      set y $rays($ray,$pt,y)
      set z $rays($ray,$pt,z)
      set x [expr {($x - $xmin) / $x_w * $max_w}]
      set y [expr {($y - $ymin) / $y_w * $max_w}]
      set z [expr {($z - $zmin) / $z_w * $max_w}]
      set rays($ray,$pt,x) $x
      set rays($ray,$pt,y) $y
      set rays($ray,$pt,z) $z
    }
  }

  # Optax.
  for {set pt 1} {$pt <= $optax(np)} {incr pt} {
    set x $optax($pt,x)
    set y $optax($pt,y)
    set z $optax($pt,z)
    set x [expr {($x - $xmin) / $x_w * $max_w}]
    set y [expr {($y - $ymin) / $y_w * $max_w}]
    set z [expr {($z - $zmin) / $z_w * $max_w}]
    set optax($pt,x) $x
    set optax($pt,y) $y
    set optax($pt,z) $z
  }

  for {set mirr 1} {$mirr <= $num_mirrors} {incr mirr} {
    for {set face 1} {$face <= 6} {incr face} {
      set np $mirrors($mirr,$face,np)
      for {set pt 1} {$pt <= $np} {incr pt} {
	set x $mirrors($mirr,$face,$pt,x)
	set y $mirrors($mirr,$face,$pt,y)
	set z $mirrors($mirr,$face,$pt,z)
	set x [expr {($x - $xmin) / $x_w * $max_w}]
	set y [expr {($y - $ymin) / $y_w * $max_w}]
	set z [expr {($z - $zmin) / $z_w * $max_w}]
	set mirrors($mirr,$face,$pt,x) $x
	set mirrors($mirr,$face,$pt,y) $y
	set mirrors($mirr,$face,$pt,z) $z
      }
    }
  }
}

#
# Scale the 3D data to fit.
#
# CHECK/UNUSED: NOT USED CURRENTLY.
#
proc scale_data2 {data_map optax_map rays_map mirrors_map} {
  upvar $data_map data
  upvar $optax_map optax
  upvar $rays_map rays
  upvar $mirrors_map mirrors

  set num_rays $data(num_rays)
  set num_mirrors $data(num_mirrors)

  set x_w [expr {$data(xmax) - $data(xmin)}]
  set y_w [expr {$data(ymax) - $data(ymin)}]
  set z_w [expr {$data(zmax) - $data(zmin)}]

  set yscale 1.0
  set zscale [expr {$y_w / $z_w}]
  set xscale [expr {$y_w / $x_w}]

  for {set ray 1} {$ray <= $num_rays} {incr ray} {
    for {set pt 1} {$pt <= $rays($ray,np)} {incr pt} {
      set x $rays($ray,$pt,x)
      set y $rays($ray,$pt,y)
      set z $rays($ray,$pt,z)
      set x [expr {$x * $xscale}]
      set y [expr {$y * $yscale}]
      set z [expr {$z * $zscale}]
      set rays($ray,$pt,x) $x
      set rays($ray,$pt,y) $y
      set rays($ray,$pt,z) $z
    }
  }

  # Optax.
  for {set pt 1} {$pt <= $optax(np)} {incr pt} {
    set x $optax($pt,x)
    set y $optax($pt,y)
    set z $optax($pt,z)
    set x [expr {$x * $xscale}]
    set y [expr {$y * $yscale}]
    set z [expr {$z * $zscale}]
    set optax($pt,x) $x
    set optax($pt,y) $y
    set optax($pt,z) $z
  }

  for {set mirr 1} {$mirr <= $num_mirrors} {incr mirr} {
    for {set face 1} {$face <= 6} {incr face} {
      set np $mirrors($mirr,$face,np)
      for {set pt 1} {$pt <= $np} {incr pt} {
	set x $mirrors($mirr,$face,$pt,x)
	set y $mirrors($mirr,$face,$pt,y)
	set z $mirrors($mirr,$face,$pt,z)
	set x [expr {$x * $xscale}]
	set y [expr {$y * $yscale}]
	set z [expr {$z * $zscale}]
	set mirrors($mirr,$face,$pt,x) $x
	set mirrors($mirr,$face,$pt,y) $y
	set mirrors($mirr,$face,$pt,z) $z
      }
    }
  }
}

#
# Populate the data sources so that the pipelines comes alive. This is
# only applicable to the "raw" format data, which reads in the raw data
# and then creates the data sources from scratch. For the "vtk" format,
# the vtkPolyDataReader does the equivalent work.
#
# Called after a new "raw" format file has been read in.
#
proc populate_data_sources {data_map optax_map rays_map mirrors_map} {
  global gvars

  upvar $data_map data
  upvar $optax_map optax
  upvar $rays_map rays
  upvar $mirrors_map mirrors

  foreach what {ray optax mirror} {
    ${what}_points Delete
    ${what}_lines Delete
  }

  vtkPoints ray_points
  vtkPoints optax_points
  vtkPoints mirror_points

  vtkCellArray optax_lines
  vtkCellArray ray_lines
  vtkCellArray mirror_lines

  ray_curve SetPoints ray_points
  ray_curve SetLines ray_lines

  optax_curve SetPoints optax_points
  optax_curve SetLines optax_lines

  mirror_curve SetPoints mirror_points
  mirror_curve SetPolys mirror_lines

  set num_rays $data(num_rays)
  set num_mirrors $data(num_mirrors)

  # Optax.
  set optax_point_size $optax(np)
  for {set pt 1} {$pt <= $optax(np)} {incr pt} {
    set x $optax($pt,x)
    set y $optax($pt,y)
    set z $optax($pt,z)
    optax_points InsertNextPoint $x $y $z
  }

  set ray_point_size 0
  for {set ray 1} {$ray <= $num_rays} {incr ray} {
    set np $rays($ray,np)
    set ray_point_size [expr {$ray_point_size + $np}]
    for {set pt 1} {$pt <= $np} {incr pt} {
      set x $rays($ray,$pt,x)
      set y $rays($ray,$pt,y)
      set z $rays($ray,$pt,z)
      ray_points InsertNextPoint $x $y $z
    }
  }

  set mirror_point_size 0
  for {set mirr 1} {$mirr <= $num_mirrors} {incr mirr} {
    for {set face 1} {$face <= 6} {incr face} {
      set np $mirrors($mirr,$face,np)
      set mirror_point_size [expr {$mirror_point_size + $np}]
      for {set pt 1} {$pt <= $np} {incr pt} {
	set x $mirrors($mirr,$face,$pt,x)
	set y $mirrors($mirr,$face,$pt,y)
	set z $mirrors($mirr,$face,$pt,z)
	mirror_points InsertNextPoint $x $y $z
      }
    }
  }

  set np $optax(np)
  optax_lines InsertNextCell $np
  for {set pt 1} {$pt <= $np} {incr pt} {
    optax_lines InsertCellPoint [expr {$pt - 1}]
  }

  for {set ray 1} {$ray <= $num_rays} {incr ray} {
    set np $rays($ray,np)
    ray_lines InsertNextCell $np
    set offset [expr {($ray - 1) * $np}]
    for {set pt 1} {$pt <= $np} {incr pt} {
      ray_lines InsertCellPoint [expr {$offset + $pt - 1}]
    }
  }

  for {set mirr 1} {$mirr <= $num_mirrors} {incr mirr} {
    set nfaces 6
    for {set face 1} {$face <= $nfaces} {incr face} {
      set np $mirrors($mirr,$face,np)
      mirror_lines InsertNextCell $np
      set offset1 [expr {($mirr - 1) * $nfaces * $np}]
      set offset [expr {$offset1 + ($face - 1) * $np}]
      for {set pt 1} {$pt <= $np} {incr pt} {
	set x $mirrors($mirr,$face,$pt,x)
	set y $mirrors($mirr,$face,$pt,y)
	set z $mirrors($mirr,$face,$pt,z)
	mirror_lines InsertCellPoint [expr {$offset + $pt - 1}]
      }
    }
  }
}

# 
# compute_minmax: Get the limits when dealing with VTK format data.
#
proc compute_minmax_vtk {data_map} {
  global gvars
  upvar $data_map data

  vtk_reader Update
  foreach {data(xmin) data(xmax) data(ymin) data(ymax) data(zmin) data(zmax)} \
    [[vtk_reader GetOutput] GetBounds] { }
}

proc display {filename data_map optax_map rays_map mirrors_map} {
  upvar $data_map data
  upvar $optax_map optax
  upvar $rays_map rays
  upvar $mirrors_map mirrors

  global gvars 

  set save_cursor [. cget -cursor]
  . configure -cursor watch
  update idletasks

  catch {$gvars(ren) RemoveActor bannerActor}
  foreach actor "$gvars(vtk_actors_raw) $gvars(vtk_actors_vtk)" {
    catch {$gvars(ren) RemoveActor $actor}
  }
  catch {$gvars(ren) RemoveActor axes_actor}
  # Also remove the axes labels.
  foreach axis {X Y Z} {
    catch {$gvars(ren) RemoveActor ${axis}_axis_actor}
  }

  # And add these actors to the renderer.

  foreach actor $gvars(vtk_actors_$gvars(reader)) {
    $gvars(ren) AddActor $actor
  }
  $gvars(ren) AddActor axes_actor
  # Also add the axes labels.
  foreach axis {X Y Z} {
    $gvars(ren) AddActor ${axis}_axis_actor
    eval ${axis}_axis_actor SetCamera [$gvars(ren) GetActiveCamera]
  }

  # For "raw" data type, we have better control over visibility and so on.
  if {$gvars(reader) == "raw"} {
    set_visibility optax_actor $gvars(optax_visible)
    set_visibility ray_actor $gvars(ray_visible)
    set_visibility mirror_actor $gvars(mirror_visible)
  }
  set_visibility axes_actor $gvars(axes_visible)

  # The coordinate axes require scaling as well since we're working 
  # with a fake coordinate space.
  foreach {gvars(xscale) gvars(yscale) gvars(zscale)} \
    [get_scale_factors data] { }

  set x_w [expr {$data(xmax) - $data(xmin)}]
  set y_w [expr {$data(ymax) - $data(ymin)}]
  set z_w [expr {$data(zmax) - $data(zmin)}]
  set max_w $x_w
  if {$y_w > $max_w} { set max_w $y_w }
  if {$z_w > $max_w} { set max_w $z_w }
  set gvars(max_w) $max_w

  set xmin_t [string trim [format "%8.5g" $data(xmin)]]
  set ymin_t [string trim [format "%8.5g" $data(ymin)]]
  set zmin_t [string trim [format "%8.5g" $data(zmin)]]
  set xmax_t [string trim [format "%8.5g" $data(xmax)]]
  set ymax_t [string trim [format "%8.5g" $data(ymax)]]
  set zmax_t [string trim [format "%8.5g" $data(zmax)]]
  $gvars(status_w) configure -text \
    "x: {$xmin_t,$xmax_t}, y: {$ymin_t,$ymax_t}, z: {$zmin_t,$zmax_t}"

  # This needs to be before set_scaling and other procs.
  set gvars(file_loaded) 1

  if {$gvars(scale_cartesian)} {
    set_scaling $gvars(max_w) $gvars(xscale) $gvars(yscale) $gvars(zscale)
  } else {
    set_scaling 1.0 1.0 1.0 1.0
  }

  eval $gvars(ren) SetBackground $gvars(background_color)

  set_default_view
  catch {global gsaved_view; unset gsaved_view}

  # show the file name in title.
  set format "$gvars(reader)"
  wm title . "SHADOW Layout Visualizer \[${format}: [file tail $filename]\]"

  $gvars(ren) ResetCamera
  $gvars(renWin) Render

  . configure -cursor $save_cursor
  update idletasks
}

proc load_file {filename} {
  global gvars 
  global g_data g_optax g_rays g_mirrors

  vputs "Loading file $filename ... "

  if {[string match {*.[vV][tT][kK]} $filename]} {
    set gvars(reader) vtk
    vtk_reader SetFileName $filename
  } else {
    set gvars(reader) raw
    if {[catch {read_data $filename data optax rays mirrors} msg]} {
      tk_dialog .error Error \
	"Error reading file \"$filename\" ($msg)." \
	error 0 Dismiss
      return
    }
  }

  array set g_data [array get data]
  array set g_optax [array get optax]
  array set g_rays [array get rays]
  array set g_mirrors [array get mirrors]

  # Update the data sources.
  if {$gvars(reader) == "raw"} {
    populate_data_sources data optax rays mirrors
  } else {
    compute_minmax_vtk data
  }

  display $filename data optax rays mirrors
}

proc show_tabular_data {data_map optax_map rays_map mirrors_map} {
  global gvars 
  global tcl_platform

  upvar $data_map data
  upvar $optax_map optax
  upvar $rays_map rays
  upvar $mirrors_map mirrors

  if {[catch {package require Tktable}]} {
    tk_dialog .shview3d {Package error} \
      "Tktable package is required for tabular data." \
      "error" 0 Dismiss
    return
  }

  set w .tabular_data
  set title "Tabular data"
  catch {destroy $w}
  toplevel $w
  wm title $w $title
  wm iconname $w $title

  set num_rays $data(num_rays)
  set num_mirrors $data(num_mirrors)
  set num_points_per_ray [expr {$num_mirrors + 2}]
  set num_points_per_optax [expr {1 + 2 * $num_mirrors}]

  set table $w.tab
  set info $w.info

  # The array displayed in the table.
  global g_tabdata
  catch {unset g_tabdata}

  set g_tabdata(1,0) OptAxis
  for {set ray 1} {$ray <= $num_rays} {incr ray} {
    set g_tabdata([expr {$ray + 1}],0) "Ray $ray"
  }
  set g_tabdata(0,1) "NP"
  for {set pt 1} {$pt <= $num_points_per_optax} {incr pt} {
    set g_tabdata(0,[expr {$pt + 1}]) "Point $pt"
  }

  # Add the optax as the first row.
  set np $optax(np)
  set row 1
  set col 1
  set g_tabdata($row,$col) $np
  for {set pt 1} {$pt <= $np} {incr pt} {
    set g_tabdata($row,[expr {$pt + 1}]) \
      [list $optax($pt,x) $optax($pt,y) $optax($pt,z)]
  }

  for {set ray 1} {$ray <= $num_rays} {incr ray} {
    set row [expr {$ray + 1}]
    set col 1
    set np $rays($ray,np)
    set g_tabdata($row,$col) $np
    for {set pt 1} {$pt <= $np} {incr pt} {
      set g_tabdata($row,[expr {$pt + 1}]) \
        [list $rays($ray,$pt,x) $rays($ray,$pt,y) $rays($ray,$pt,z)]
    }
  }

  frame $info -relief sunken -bd 2
  label $info.active_label -text "Value: "
  entry $info.active_value -textvariable g_tabdata(active) -state disabled
  pack $info.active_label -side left 
  pack $info.active_value -side left -expand yes -fill x

  table $table \
    -variable g_tabdata \
    -titlerows 1 -titlecols 1 \
    -cols [expr {$num_points_per_optax + 2}] \
    -rows [expr {$num_rays + 1 + 1}] \
    -state disabled \
    -anchor nw

  pack $info -side top -fill x -expand yes -padx 5 -pady 5
  pack $table -side top -fill both -expand yes -padx 5 -pady 5

  # Withdraw the window, then update all the geometry information
  # so we know how big it wants to be, then center the window in the
  # display and de-iconify it.

  wm withdraw $w
  update idletasks
  set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
	  - [winfo vrootx [winfo parent $w]]]
  set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
	  - [winfo vrooty [winfo parent $w]]]
  wm geom $w +$x+$y
  update idle
  wm deiconify $w

  raise $w
  focus $w
}

#
# **** Only works for VTK >= 4.0 ****
#
proc export_image {format filename} {
  global gvars 
  set filters(EPS)	PostScript
  set filters(JPEG)	JPEG
  set filters(TIFF)	TIFF
  set filters(PPM)	PNM

  if {![info exists filters($format)]} {
    tk_dialog .shview3d {Image export error} \
      "Exporting $format format is not supported, sorry." \
      "error" 0 Dismiss
    return
  }

  vputs stderr "Exporting image to $format file $filename ... "

  set filter_name [set filters($format)]
  set filter vtk${filter_name}Writer
  set writer ${format}_writer

  if {[info command $writer] == ""} {
    $filter $writer
  }
  if {[info command w2if] == ""} {
    vtkWindowToImageFilter w2if
    w2if SetInput $gvars(renWin)
  }

  raise .
  update
  w2if Update
  $writer SetFileName "$filename"
  $writer SetInput [w2if GetOutput]
  $writer Write
}

proc export_PPM {filename} {
  global gvars 

  vputs stderr "Printing to PPM file $filename ... "

  if {[expr {$gvars(vtk_major_version) < 4}]} {
    raise .
    update
    $gvars(renWin) SetFileName "$filename"
    $gvars(renWin) SaveImageAsPPM
  } else {
    export_image PPM $filename
  }
}

proc export_EPS {filename} {
  global gvars 

  vputs stderr "Printing to EPS file $filename ... "

  if {[expr {$gvars(vtk_major_version) < 4}]} {
    tk_dialog .shview3d {EPS export error} \
      "Shview3d requires VTK 4.0 or newer to export EPS format." \
      "error" 0 Dismiss
    return
  } else {
    export_image EPS $filename
  }
}

proc export_JPEG {filename} {
  global gvars 

  vputs stderr "Printing to JPEG file $filename ... "

  if {[expr {$gvars(vtk_major_version) < 4}]} {
    tk_dialog .shview3d {JPEG export error} \
      "Shview3d requires VTK 4.0 or newer to export JPEG format." \
      "error" 0 Dismiss
    return
  } else {
    export_image JPEG $filename
  }
}

proc export_TIFF {filename} {
  global gvars 

  vputs stderr "Printing to TIFF file $filename ... "

  if {[expr {$gvars(vtk_major_version) < 4}]} {
    tk_dialog .shview3d {TIFF export error} \
      "Shview3d requires VTK 4.0 or newer to export TIFF format." \
      "error" 0 Dismiss
    return
  } else {
    export_image TIFF $filename
  }
}

proc export_VTK {filename} {
  global gvars 

  vputs stderr "Printing to VTK file $filename ... "
  vtkAppendPolyData apf
  apf AddInput optax_curve
  apf AddInput ray_curve
  apf AddInput mirror_curve
  vtkPolyDataWriter vtk
  vtk SetInput [apf GetOutput]
  vtk SetFileName $filename
  vtk Write
}

proc export_VRML {filename} {
  global gvars 

  vputs stderr "Printing to VRML file $filename ... "

  vtkVRMLExporter vrml
  vrml SetInput $gvars(renWin)
  vrml SetStartWrite "vrml SetFileName $filename"
  vrml SetEndWrite "vrml SetFileName /a/a${filename}"
  vrml Write
}

# set visibility
proc set_visibility {actor {visible 1}} {
  global gvars

  if {$visible} {
    set op VisibilityOn
  } else {
    set op VisibilityOff
  }

  eval $actor $op
  if {$actor == "axes_actor"} {
    foreach axis {X Y Z} {
      set actor ${axis}_axis_actor
      ${axis}_axis_actor $op
    }
  }
}

proc set_scaling {max_w xscale yscale zscale} {
  global gvars

  if {! $gvars(file_loaded)} {
    # Silently return. Take effect after a file is loaded.
    # The other choice is to (1) warn about no data, (2) toggle the
    # variable, and (3) then return. Too much trouble.
    return
  }

  foreach actor $gvars(vtk_actors_$gvars(reader)) {
    $actor SetScale $xscale $yscale $zscale
  }
  set xorigin [expr {-0.5 * $max_w}]
  axes SetScaleFactor $max_w
  axes SetOrigin $xorigin 0 0
  # Now the axes labels.
  foreach axis {X Y Z} {
    set actor ${axis}_axis_actor
    set factor [expr {$max_w / 20.0}]
    ${axis}_axis_actor SetScale $factor $factor $factor
  }
  # Place the Axes labels -- note that our X origin is not the expected
  # point.
  set xpos [expr {0.5 * $max_w + $max_w * 0.05}]
  set ypos [expr {$max_w + 0.05 * $max_w}]
  set zpos [expr {$max_w + 0.05 * $max_w}]
  set origin [expr {-0.5 * $max_w}]
  X_axis_actor SetPosition $xpos    0       0
  Y_axis_actor SetPosition $origin  $ypos   0
  Z_axis_actor SetPosition $origin  0       $zpos
}

### VISUALIZATION MODULE


##
## MENU CALLBACKS
##

# Procedure opens file and resets view
proc menu_cmd:open_file {} {
  set types {
    {{SHADOW raw layout data}			{.dat}	}
    {{SHADOW VTK layout data}			{.vtk}	}
    {{All Files}				{*}	}
  }
  set filename [tk_getOpenFile -filetypes $types]
  if { $filename != "" } {
    load_file $filename
  }
}

# Procedure prints file 
proc menu_cmd:export {format} {
  set file_ext(EPS)	".eps"
  set file_ext(JPEG)	".jpg"
  set file_ext(TIFF)	".tif"
  set file_ext(PPM)	".ppm"
  set file_ext(VTK)	".vtk"
  set file_ext(VRML)	".wrl"
  set ext $file_ext($format)
  set types [list \
    [list "$format format"		$ext     ] \
    [list {All Files }			{*}      ] \
  ]
  set filename [tk_getSaveFile -defaultextension $ext -filetypes $types]
  if { $filename != "" } {
    export_$format $filename
  }
}

# Procedure to set particular views
proc menu_cmd:update_view {x y z vx vy vz} {
  global gvars

  set camera [$gvars(ren) GetActiveCamera]
  if {[expr {$gvars(vtk_major_version) < 4}]} {
    $camera SetViewPlaneNormal $x $y $z
  }
  $camera SetViewUp $vx $vy $vz
  $gvars(ren) ResetCamera
  Render [$gvars(ren) GetRenderWindow]
}

# Procedure to set particular views
proc set_default_view {} {
  global gvars

  ##############
  # Clipping Range: 213.185 10659.3
  # Distance: 2131.85
  # Eye Angle: 2
  # Focal Disk: 1
  # Focal Point: 250 250 250
  # Position: 685.188 -873.748 2008.58
  # Parallel Projection:  0
  # Thickness: 10446.1
  # View Angle: 24
  # View Plane Normal: 0.204137 -0.527124 0.824905
  # View Up: -0.62941 0.574719 0.523011
  ##############

  set camera [$gvars(ren) GetActiveCamera]
  $gvars(ren) ResetCamera
  $camera SetClippingRange 213.185 10659.3
  $camera SetDistance 2131.85
  $camera SetEyeAngle 2
  $camera SetFocalDisk 1
  $camera SetFocalPoint 250 250 250
  $camera SetPosition 685.188 -873.748 2008.58
  $camera SetParallelProjection 0
  $camera SetThickness 10446.1
  $camera SetViewAngle 24
  if {[expr {$gvars(vtk_major_version) < 4}]} {
    $camera SetViewPlaneNormal 0.204137 -0.527124 0.824905
  }
  $camera SetViewUp -0.62941 0.574719 0.523011
}

# Procedure to set particular views
proc menu_cmd:default_view {} {
  set_default_view
  global gvars
  $gvars(ren) ResetCamera
  $gvars(renWin) Render
}

## Procedures to save and restore views
##
## Save view in global gsaved_view array. 
##
proc menu_cmd:save_view_to_clipboard {} {
  global gvars gsaved_view

  set camera [$gvars(ren) GetActiveCamera]

  set gsaved_view(ClippingRange) "[$camera GetClippingRange]"
  set gsaved_view(Distance) "[$camera GetDistance]"
  set gsaved_view(EyeAngle) "[$camera GetEyeAngle]"
  set gsaved_view(FocalDisk) "[$camera GetFocalDisk]"
  set gsaved_view(FocalPoint) "[$camera GetFocalPoint]"
  set gsaved_view(Position) "[$camera GetPosition]"
  set gsaved_view(ParallelProjection) \
    "[expr int([$camera GetParallelProjection])]"
  set gsaved_view(Thickness) "[$camera GetThickness]"
  set gsaved_view(ViewAngle) "[$camera GetViewAngle]"
  set gsaved_view(ViewPlaneNormal) "[$camera GetViewPlaneNormal]"
  set gsaved_view(ViewUp) "[$camera GetViewUp]"
}

## Recover view from gsaved_view array.
proc menu_cmd:restore_view_from_clipboard {} {
  global gvars gsaved_view

  if ![info exists gsaved_view] {
    tk_dialog .warning_dlg Warning \
      "View clipboard is empty" \
      warning 0 Dismiss
    return
  }

  set camera [$gvars(ren) GetActiveCamera]

  foreach var [array names gsaved_view] {
    set val "[set gsaved_view($var)]"
    eval $camera Set$var $val
  }

  ###
  # No idea why one works better than the other one. Pure trial and error
  # here.
  ###
  #$gvars(renWin) Render
  Render [$gvars(ren) GetRenderWindow]
}

##
## Save view in simple ascii file. 
##
proc menu_cmd:save_view {{filename ""}} {
  if {$filename == ""} {
    set types {
        {{vtk Camera }  {.cam}        }
        {{All Files }   *             }
    }
    set filename [tk_getSaveFile -filetypes $types]
  }

  if { $filename != "" } {
    if {[catch {open $filename w} fp]} {
      tk_dialog .error Error \
	"Error opening camera file \"$filename\" ($fp)." \
	error 0 Dismiss
    } else {
      global gvars
      set camera [$gvars(ren) GetActiveCamera]
      puts $fp "# vtk CameraFile Version 2.0"
      puts $fp "Clipping Range: [$camera GetClippingRange]"
      puts $fp "Distance: [$camera GetDistance]"
      puts $fp "Eye Angle: [$camera GetEyeAngle]"
      puts $fp "Focal Disk: [$camera GetFocalDisk]"
      puts $fp "Focal Point: [$camera GetFocalPoint]"
      puts $fp "Position: [$camera GetPosition]"
      puts $fp "Parallel Projection: \
	      [expr int([$camera GetParallelProjection])]"
      puts $fp "Thickness: [$camera GetThickness]"
      puts $fp "View Angle: [$camera GetViewAngle]"
      puts $fp "View Plane Normal: [$camera GetViewPlaneNormal]"
      puts $fp "View Up: [$camera GetViewUp]"
      close $fp
    }
  }
}

## Recover view from saved file
proc menu_cmd:restore_view {{filename ""}} {
  global gvars
  if {$filename == "" } {
    set types {
        {{vtk Camera }  {.cam}        }
        {{All Files }   *             }
    }
    set filename [tk_getOpenFile -filetypes $types]
  }

  if { $filename != "" } {
    if {[catch {open $filename r} fp]} {
      tk_dialog .error Error \
	"Error opening input camera file \"$filename\" ($fp)." \
	error 0 Dismiss
    } else {
      set line [read $fp]

      if { [string first "# vtk CameraFile Version 2.0" $line] == -1} {
	tk_dialog .error Error \
	  "Illegal camera file format. Can't find magic cookie." \
	  error 0 Dismiss
      } else {
        set camera [$gvars(ren) GetActiveCamera]

	set loc [string first "Clipping Range:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 15] end]
	  scan $substr "%f %f" min max
	  $camera SetClippingRange $min $max
	}

	set loc [string first "Distance:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 9] end]
	  scan $substr "%f" distance
	  $camera SetDistance $distance
	}

	set loc [string first "Eye Angle:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 10] end]
	  scan $substr "%f" angle
	  $camera SetEyeAngle $angle
	}

	set loc [string first "Focal Disk:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 11] end]
	  scan $substr "%f" fd
	  $camera SetFocalDisk $fd
	}

	set loc [string first "Focal Point:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 12] end]
	  scan $substr "%f %f %f" fp1 fp2 fp3
	  $camera SetFocalPoint $fp1 $fp2 $fp3
	}

	set loc [string first "Position:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 9] end]
	  scan $substr "%f %f %f" p1 p2 p3
	  $camera SetPosition $p1 $p2 $p3
	}

	set loc [string first "Parallel Projection:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 20] end]
	  scan $substr "%d" pp
	  $camera SetParallelProjection $pp
	}

	set loc [string first "Thickness:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 10] end]
	  scan $substr "%f" t
	  $camera SetThickness $t
	}

	set loc [string first "View Angle:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 11] end]
	  scan $substr "%f" va
	  $camera SetViewAngle $va
	}

	set loc [string first "View Plane Normal:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 18] end]
	  scan $substr "%f %f %f" vpn1 vpn2 vpn3
	  if {[expr {$gvars(vtk_major_version) < 4}]} {
	    $camera SetViewPlaneNormal $vpn1 $vpn2 $vpn3
	  }
	}

	set loc [string first "View Up:" $line]
	if { $loc != -1 } {
	  set substr [string range $line [expr $loc + 8] end]
	  scan $substr "%f %f %f" vup1 vup2 vup3
	  $camera SetViewUp $vup1 $vup2 $vup3
	}
      }
    }
  }
  # Render [$gvars(ren) GetRenderWindow]
  $gvars(renWin) Render
}

# change visibility
proc menu_cmd:change_visibility {what} {
  global gvars

  set_visibility ${what}_actor $gvars(${what}_visible)

  $gvars(renWin) Render
}

# change scaling
proc menu_cmd:change_scaling {} {
  global gvars

  if {$gvars(scale_cartesian)} {
    set xscale $gvars(xscale)
    set yscale $gvars(yscale)
    set zscale $gvars(zscale)
    set max_w $gvars(max_w)
  } else {
    set xscale 1.0
    set yscale 1.0
    set zscale 1.0
    set max_w 1.0
  }
  set_scaling $max_w $xscale $yscale $zscale
  $gvars(renWin) Render
}

# change colors
proc menu_cmd:change_color {what} {
  global gvars

  set names(optax)	"Optical Axis" 
  set names(ray)	"Rays" 
  set names(mirror)	"Mirrors" 
  set names(axes)	"Coordinate Axes" 
  set names(background)	"Background" 

  set name $names($what)

  set new_color [color_dialog . $name $gvars(${what}_color)]
  if { $new_color == "" } { return }

  set gvars(${what}_color) $new_color

  if {$what == "background"} {
    eval $gvars(ren) SetBackground $gvars(background_color)
  } else {
    set property [${what}_actor GetProperty]
    eval $property SetColor $gvars(${what}_color)
    if {$what == "axes"} {
      foreach axis {X Y Z} {
	set actor ${axis}_axis_actor
	set property [$actor GetProperty]
	eval $property SetColor $gvars(${what}_color)
      }
    }
  }
  $gvars(renWin) Render
}

# change opacity
proc menu_cmd:change_opacity {what} {
  global gvars

  set names(optax)	"Optical Axis" 
  set names(ray)	"Rays" 
  set names(mirror)	"Mirrors" 
  set names(axes)	"Coordinate Axes" 

  set name $names($what)

  set new_opacity [opacity_dialog . "Set $name Opacity" $gvars(${what}_opacity)]
  if { $new_opacity == "" } { return }

  if {[catch {expr {$new_opacity + 0.0}}]} {
    tk_dialog .error Error \
      "Illegal opacity value $new_opacity. Must be a floating point number." \
      error 0 Dismiss
    return
  }

  if {[expr {$new_opacity < 0.0 || $new_opacity > 1.0}]} {
    tk_dialog .error Error \
      "Illegal opacity value $new_opacity. Must be between 0.0 and 1.0." \
      error 0 Dismiss
    return
  }
  set gvars(${what}_opacity) $new_opacity

  set property [${what}_actor GetProperty]
  eval $property SetOpacity $gvars(${what}_opacity)
  if {$what == "axes"} {
    foreach axis {X Y Z} {
      set actor ${axis}_axis_actor
      set property [$actor GetProperty]
      eval $property SetOpacity $gvars(${what}_opacity)
    }
  }
  $gvars(renWin) Render
}

# Show data in tabular form.
proc menu_cmd:show_data {} {
  global gvars

  if {! $gvars(file_loaded)} {
    tk_dialog .info Info \
      "No file loaded!" \
      info 0 Dismiss
    return
  }

  if {$gvars(reader) != "raw"} {
    tk_dialog .info Info \
      "Can only show data when input file is in the \"raw\" format." \
      info 0 Dismiss
    return
  }

  global g_data g_optax g_rays g_mirrors
  show_tabular_data g_data g_optax g_rays g_mirrors
}

# View dimensions
proc menu_cmd:view_dimensions {} {
  global gvars

  tk_dialog .info Info \
    "\"View Dimensions\" command not yet implemented, sorry." \
    info 0 Dismiss
  return
}

# Exit
proc menu_cmd:exit {{status 0}} {
  global gvars

  if {[expr {$gvars(vtk_major_version) >= 4}]} {
    vtkCommand DeleteAllObjects
  }
  exit $status
}

##
## DIALOG helpers.
##

proc toolset_dialog {w title text fields bitmap default args} {
  global toolsetPriv tcl_platform tk_version

  # 1. Create the top-level window and divide it into top
  # and bottom parts.

  catch {destroy $w}
  toplevel $w -class Dialog
  wm title $w $title
  wm iconname $w Dialog
  wm protocol $w WM_DELETE_WINDOW { }

  # The following command means that the dialog won't be posted if
  # [winfo parent $w] is iconified, but it's really needed;  otherwise
  # the dialog can become obscured by other windows in the application,
  # even though its grab keeps the rest of the application from being used.

  wm transient $w [winfo toplevel [winfo parent $w]]
  if {$tcl_platform(platform) == "macintosh"} {
    unsupported1 style $w dBoxProc
  }

  if [llength $fields] {set has_fields 1} {set has_fields 0}

  frame $w.bot
  if $has_fields {frame $w.middle}
  frame $w.top
  if {$tcl_platform(platform) == "unix"} {
    $w.bot configure -relief raised -bd 1
    if $has_fields {$w.middle configure -relief raised -bd 1}
    $w.top configure -relief raised -bd 1
  }
  pack $w.bot -side bottom -fill both
  pack $w.top -side top -fill both -expand 1
  if $has_fields {pack $w.middle -side top -fill both -expand 1}

  # 2. Fill the top part with bitmap and message (use the option
  # database for -wraplength so that it can be overridden by
  # the caller).

  option add *Dialog.msg.wrapLength 3i widgetDefault
  label $w.msg -justify left -text $text
  if {$tcl_platform(platform) == "macintosh"} {
    $w.msg configure -font system
  } else {
    if { $tk_version < 8.0 } {
      set font -Adobe-Times-Medium-R-Normal--*-180-*-*-*-*-*-*
    } else {
      set font {Times 18}
    }
    catch {$w.msg configure -font $font}
  }
  pack $w.msg -in $w.top -side right -expand 1 -fill both -padx 3m -pady 3m
  if {$bitmap != ""} {
    if {($tcl_platform(platform) == "macintosh") && ($bitmap == "error")} {
      set bitmap "stop"
    }
    label $w.bitmap -bitmap $bitmap
    pack $w.bitmap -in $w.top -side left -padx 3m -pady 3m
  }

  # Do the individual fields now.
  if $has_fields {
      set prompt_frame [frame $w.middle.prompts]
      set entry_frame [frame $w.middle.entries]
  }

  set i 1
  foreach field $fields {
    set this_text	[lindex $field 0]
    set this_default	[lindex $field 1]
    set this_label	$prompt_frame.label$i
    set this_value	$entry_frame.field$i
  
    label $this_label -justify left -text $this_text

    set toolsetPriv(field$i) $this_default
    entry $this_value -text toolsetPriv(field$i)
  
    pack $this_label -side top -expand 1
    pack $this_value -side top -expand 1 -fill both

    incr i
  }

  if $has_fields {
    pack $prompt_frame -side left -expand 1
    pack $entry_frame -side left -expand 1 -fill both
  }

  # 3. Create a row of buttons at the bottom of the dialog.

  set i 0
  foreach but $args {
    button $w.button$i -text $but -command "set toolsetPriv(button) $i"
    if { $tk_version >= 8.0 } {
      if {$i == $default} {
	$w.button$i configure -default active
      } else {
	$w.button$i configure -default normal
      }
    }
    grid $w.button$i -in $w.bot -column $i -row 0 -sticky ew -padx 10
    grid columnconfigure $w.bot $i
    # We boost the size of some Mac buttons for l&f
    if {$tcl_platform(platform) == "macintosh"} {
      set tmp [string tolower $but]
      if {($tmp == "ok") || ($tmp == "cancel")} {
	grid columnconfigure $w.bot $i -minsize [expr 59 + 20]
      }
    }
    incr i
  }

  # 4. Create a binding for <Return> on the dialog if there is a
  # default button. Always create an <Escape> binding however.

  if {$default >= 0} {
    bind $w <Return> "
	$w.button$default configure -state active -relief sunken
	update idletasks
	after 100
	set toolsetPriv(button) $default
    "
  }
  bind $w <Escape> {set toolsetPriv(button) -1}

  # 5. Create a <Destroy> binding for the window that sets the
  # button variable to -1;  this is needed in case something happens
  # that destroys the window, such as its parent window being destroyed.

  bind $w <Destroy> {set toolsetPriv(button) -1}

  # 6. Withdraw the window, then update all the geometry information
  # so we know how big it wants to be, then center the window in the
  # display and de-iconify it.

  wm withdraw $w
  update idletasks
  set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
	  - [winfo vrootx [winfo parent $w]]]
  set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
	  - [winfo vrooty [winfo parent $w]]]
  wm geom $w +$x+$y
  update idle
  wm deiconify $w

  # 7. Set a grab and claim the focus too.

  set oldFocus [focus]
  set oldGrab [grab current $w]
  if {$oldGrab != ""} {
    set grabStatus [grab status $oldGrab]
  }
  grab $w
  if {$default >= 0} {
    focus $w.button$default
  } else {
    focus $w
  }

  # 8. Wait for the user to respond, then restore the focus and
  # return the index of the selected button.  Restore the focus
  # before deleting the window, since otherwise the window manager
  # may take the focus away so we can't redirect it.  Finally,
  # restore any grab that was in effect.

  tkwait variable toolsetPriv(button)
  catch {focus $oldFocus}
  catch {
    # It's possible that the window has already been destroyed,
    # hence this "catch".  Delete the Destroy handler so that
    # toolsetPriv(button) doesn't get reset by it.

    bind $w <Destroy> {}
    destroy $w
  }
  if {$oldGrab != ""} {
    if {$grabStatus == "global"} {
      grab -global $oldGrab
    } else {
      grab $oldGrab
    }
  }
  if $has_fields {
    set retstuff $toolsetPriv(button)
    set i 1
    foreach field $fields {
      set value [set toolsetPriv(field$i)]
      lappend retstuff $value
      incr i
    }
    return $retstuff
  } else {
    return $toolsetPriv(button)
  }
}

#
# Prompt and get opacity. Returns "" if cancelled.
#
proc opacity_dialog {w title cur_opacity} {
  set msg "Enter the opacity value between 0.0 and 1.0."
  set retstuff [toolset_dialog .dlg "$title" $msg \
    "{Opacity: $cur_opacity} "  \
    question 0 OK Cancel]
  set button [lindex $retstuff 0]
  if {$button == 0} {
    set retvalue [lrange $retstuff 1 end]
  } else {
    set retvalue ""
  }
  return $retvalue
}

# change colors
#
# use tk_chooseColor and then translates the rgb values to [0.0 ... 1.0]
# range suitable for VTK.
#
proc color_dialog {w title cur_color} {
  set r [expr int(65535.0 * [lindex $cur_color 0])]
  set g [expr int(65535.0 * [lindex $cur_color 1])]
  set b [expr int(65535.0 * [lindex $cur_color 2])]
  set initial_color [format #%04x%04x%04x $r $g $b]

  vputs "initialize_color = $initial_color ($r $g $b)"
  
  set new_color [tk_chooseColor \
		 -title $title \
                 -initialcolor $initial_color]
  
  if { $new_color == "" } { return "" }
  
  set new_color2 [winfo rgb . $new_color]
  vputs "New color: choosecolor => $new_color, winfo => $new_color2"

  set r [expr [lindex $new_color2 0]/65535.0] 
  set g [expr [lindex $new_color2 1]/65535.0]
  set b [expr [lindex $new_color2 2]/65535.0]
  set new_color2 [list $r $g $b]
  vputs "New color is $new_color = $new_color2 ($r $g $b)"
  return $new_color2
}

##
## UTILITY routines.
##

# verbose mode puts
proc vputs {args} {
  global gvars
  if {$gvars(verbose)} {
    puts stderr "devviewer: [join $args]"
  }
}

##
## MAIN PROGRAM. Called at the end of the file.
##

proc run_main {} {
  global argv

  initialize
  update

  if {[llength $argv]} {
    set arg [lindex $argv 0]
    load_file $arg
  }
}

#### 
#
# START VTK Internal Tcl Files
#
# VTK internal Tcl files wrapped for transport. UPDATE these files for
# each new release.
#
# colors.tcl       -> initialize_vtk_colors
# vtkInt.tcl       -> initialize_vtk_internal
# TkInteractor.tcl -> initialize_vtk_interactor
# WidgetObject.tcl -> initialize_vtk_widget
#
# The only change is made in TkInteractor.tcl portion, where the source
# command is replaced by calls to the wrapped procs.
# 
# initialize_modules initializes all the necessary components. 
#
# CHECK/UPDATE: Current versions are from VTK 3.1.2 release
# distribution.
#
# Check for MK CHANGE and CHECK/UPDATE blocks to see what local changes 
# I've had to make for backward compatibility to VTK 2.2.
#

#
# FIXME/TODO: Obsolete this for VTK versions >= 4.0.
#
# Verbatim copy of colors.tcl.
#
proc initialize_vtk_colors {} {
  set text {

  #  Whites
  set antique_white "0.9804 0.9216 0.8431"
  set azure "0.9412 1.0000 1.0000"
  set bisque "1.0000 0.8941 0.7686"
  set blanched_almond "1.0000 0.9216 0.8039"
  set cornsilk "1.0000 0.9725 0.8627"
  set eggshell "0.9900 0.9000 0.7900"
  set floral_white "1.0000 0.9804 0.9412"
  set gainsboro "0.8627 0.8627 0.8627"
  set ghost_white "0.9725 0.9725 1.0000"
  set honeydew "0.9412 1.0000 0.9412"
  set ivory "1.0000 1.0000 0.9412"
  set lavender "0.9020 0.9020 0.9804"
  set lavender_blush "1.0000 0.9412 0.9608"
  set lemon_chiffon "1.0000 0.9804 0.8039"
  set linen "0.9804 0.9412 0.9020"
  set mint_cream "0.9608 1.0000 0.9804"
  set misty_rose "1.0000 0.8941 0.8824"
  set moccasin "1.0000 0.8941 0.7098"
  set navajo_white "1.0000 0.8706 0.6784"
  set old_lace "0.9922 0.9608 0.9020"
  set papaya_whip "1.0000 0.9373 0.8353"
  set peach_puff "1.0000 0.8549 0.7255"
  set seashell "1.0000 0.9608 0.9333"
  set snow "1.0000 0.9804 0.9804"
  set thistle "0.8471 0.7490 0.8471"
  set titanium_white "0.9900 1.0000 0.9400"
  set wheat "0.9608 0.8706 0.7020"
  set white "1.0000 1.0000 1.0000"
  set white_smoke "0.9608 0.9608 0.9608"
  set zinc_white "0.9900 0.9700 1.0000"

  #  Greys
  set cold_grey "0.5000 0.5400 0.5300"
  set dim_grey "0.4118 0.4118 0.4118"
  set grey "0.7529 0.7529 0.7529"
  set light_grey "0.8275 0.8275 0.8275"
  set slate_grey "0.4392 0.5020 0.5647"
  set slate_grey_dark "0.1843 0.3098 0.3098"
  set slate_grey_light "0.4667 0.5333 0.6000"
  set warm_grey "0.5000 0.5000 0.4100"

  #  Blacks
  set black "0.0000 0.0000 0.0000"
  set ivory_black "0.1600 0.1400 0.1300"
  set lamp_black "0.1800 0.2800 0.2300"

  #  Reds
  set alizarin_crimson "0.8900 0.1500 0.2100"
  set brick "0.6100 0.4000 0.1200"
  set cadmium_red_deep "0.8900 0.0900 0.0500"
  set coral "1.0000 0.4980 0.3137"
  set coral_light "0.9412 0.5020 0.5020"
  set deep_pink "1.0000 0.0784 0.5765"
  set english_red "0.8300 0.2400 0.1000"
  set firebrick "0.6980 0.1333 0.1333"
  set geranium_lake "0.8900 0.0700 0.1900"
  set hot_pink "1.0000 0.4118 0.7059"
  set indian_red "0.6900 0.0900 0.1200"
  set light_salmon "1.0000 0.6275 0.4784"
  set madder_lake_deep "0.8900 0.1800 0.1900"
  set maroon "0.6902 0.1882 0.3765"
  set pink "1.0000 0.7529 0.7961"
  set pink_light "1.0000 0.7137 0.7569"
  set raspberry "0.5300 0.1500 0.3400"
  set red "1.0000 0.0000 0.0000"
  set rose_madder "0.8900 0.2100 0.2200"
  set salmon "0.9804 0.5020 0.4471"
  set tomato "1.0000 0.3882 0.2784"
  set venetian_red "0.8300 0.1000 0.1200"

  #  Browns
  set beige "0.6400 0.5800 0.5000"
  set brown "0.5000 0.1647 0.1647"
  set brown_madder "0.8600 0.1600 0.1600"
  set brown_ochre "0.5300 0.2600 0.1200"
  set burlywood "0.8706 0.7216 0.5294"
  set burnt_sienna "0.5400 0.2100 0.0600"
  set burnt_umber "0.5400 0.2000 0.1400"
  set chocolate "0.8235 0.4118 0.1176"
  set deep_ochre "0.4500 0.2400 0.1000"
  set flesh "1.0000 0.4900 0.2500"
  set flesh_ochre "1.0000 0.3400 0.1300"
  set gold_ochre "0.7800 0.4700 0.1500"
  set greenish_umber "1.0000 0.2400 0.0500"
  set khaki "0.9412 0.9020 0.5490"
  set khaki_dark "0.7412 0.7176 0.4196"
  set light_beige "0.9608 0.9608 0.8627"
  set peru "0.8039 0.5216 0.2471"
  set rosy_brown "0.7373 0.5608 0.5608"
  set raw_sienna "0.7800 0.3800 0.0800"
  set raw_umber "0.4500 0.2900 0.0700"
  set sepia "0.3700 0.1500 0.0700"
  set sienna "0.6275 0.3216 0.1765"
  set saddle_brown "0.5451 0.2706 0.0745"
  set sandy_brown "0.9569 0.6431 0.3765"
  set tan "0.8235 0.7059 0.5490"
  set van_dyke_brown "0.3700 0.1500 0.0200"

  #  Oranges
  set cadmium_orange "1.0000 0.3800 0.0100"
  set cadmium_red_light "1.0000 0.0100 0.0500"
  set carrot "0.9300 0.5700 0.1300"
  set dark_orange "1.0000 0.5490 0.0000"
  set mars_orange "0.5900 0.2700 0.0800"
  set mars_yellow "0.8900 0.4400 0.1000"
  set orange "1.0000 0.5000 0.0000"
  set orange_red "1.0000 0.2706 0.0000"
  set yellow_ochre "0.8900 0.5100 0.0900"

  #  Yellows
  set aureoline_yellow "1.0000 0.6600 0.1400"
  set banana "0.8900 0.8100 0.3400"
  set cadmium_lemon "1.0000 0.8900 0.0100"
  set cadmium_yellow "1.0000 0.6000 0.0700"
  set cadmium_yellow_light "1.0000 0.6900 0.0600"
  set gold "1.0000 0.8431 0.0000"
  set goldenrod "0.8549 0.6471 0.1255"
  set goldenrod_dark "0.7216 0.5255 0.0431"
  set goldenrod_light "0.9804 0.9804 0.8235"
  set goldenrod_pale "0.9333 0.9098 0.6667"
  set light_goldenrod "0.9333 0.8667 0.5098"
  set melon "0.8900 0.6600 0.4100"
  set naples_yellow_deep "1.0000 0.6600 0.0700"
  set yellow "1.0000 1.0000 0.0000"
  set yellow_light "1.0000 1.0000 0.8784"

  #  Greens
  set chartreuse "0.4980 1.0000 0.0000"
  set chrome_oxide_green "0.4000 0.5000 0.0800"
  set cinnabar_green "0.3800 0.7000 0.1600"
  set cobalt_green "0.2400 0.5700 0.2500"
  set emerald_green "0.0000 0.7900 0.3400"
  set forest_green "0.1333 0.5451 0.1333"
  set green "0.0000 1.0000 0.0000"
  set green_dark "0.0000 0.3922 0.0000"
  set green_pale "0.5961 0.9843 0.5961"
  set green_yellow "0.6784 1.0000 0.1843"
  set lawn_green "0.4863 0.9882 0.0000"
  set lime_green "0.1961 0.8039 0.1961"
  set mint "0.7400 0.9900 0.7900"
  set olive "0.2300 0.3700 0.1700"
  set olive_drab "0.4196 0.5569 0.1373"
  set olive_green_dark "0.3333 0.4196 0.1843"
  set permanent_green "0.0400 0.7900 0.1700"
  set sap_green "0.1900 0.5000 0.0800"
  set sea_green "0.1804 0.5451 0.3412"
  set sea_green_dark "0.5608 0.7373 0.5608"
  set sea_green_medium "0.2353 0.7020 0.4431"
  set sea_green_light "0.1255 0.6980 0.6667"
  set spring_green "0.0000 1.0000 0.4980"
  set spring_green_medium "0.0000 0.9804 0.6039"
  set terre_verte "0.2200 0.3700 0.0600"
  set viridian_light "0.4300 1.0000 0.4400"
  set yellow_green "0.6039 0.8039 0.1961"

  #  Cyans
  set aquamarine "0.4980 1.0000 0.8314"
  set aquamarine_medium "0.4000 0.8039 0.6667"
  set cyan "0.0000 1.0000 1.0000"
  set cyan_white "0.8784 1.0000 1.0000"
  set turquoise "0.2510 0.8784 0.8157"
  set turquoise_dark "0.0000 0.8078 0.8196"
  set turquoise_medium "0.2824 0.8196 0.8000"
  set turquoise_pale "0.6863 0.9333 0.9333"

  #  Blues
  set alice_blue "0.9412 0.9725 1.0000"
  set blue "0.0000 0.0000 1.0000"
  set blue_light "0.6784 0.8471 0.9020"
  set blue_medium "0.0000 0.0000 0.8039"
  set cadet "0.3725 0.6196 0.6275"
  set cobalt "0.2400 0.3500 0.6700"
  set cornflower "0.3922 0.5843 0.9294"
  set cerulean "0.0200 0.7200 0.8000"
  set dodger_blue "0.1176 0.5647 1.0000"
  set indigo "0.0300 0.1800 0.3300"
  set manganese_blue "0.0100 0.6600 0.6200"
  set midnight_blue "0.0980 0.0980 0.4392"
  set navy "0.0000 0.0000 0.5020"
  set peacock "0.2000 0.6300 0.7900"
  set powder_blue "0.6902 0.8784 0.9020"
  set royal_blue "0.2549 0.4118 0.8824"
  set slate_blue "0.4157 0.3529 0.8039"
  set slate_blue_dark "0.2824 0.2392 0.5451"
  set slate_blue_light "0.5176 0.4392 1.0000"
  set slate_blue_medium "0.4824 0.4078 0.9333"
  set sky_blue "0.5294 0.8078 0.9216"
  set sky_blue_deep "0.0000 0.7490 1.0000"
  set sky_blue_light "0.5294 0.8078 0.9804"
  set steel_blue "0.2745 0.5098 0.7059"
  set steel_blue_light "0.6902 0.7686 0.8706"
  set turquoise_blue "0.0000 0.7800 0.5500"
  set ultramarine "0.0700 0.0400 0.5600"

  #  Magentas
  set blue_violet "0.5412 0.1686 0.8863"
  set cobalt_violet_deep "0.5700 0.1300 0.6200"
  set magenta "1.0000 0.0000 1.0000"
  set orchid "0.8549 0.4392 0.8392"
  set orchid_dark "0.6000 0.1961 0.8000"
  set orchid_medium "0.7294 0.3333 0.8275"
  set permanent_red_violet "0.8600 0.1500 0.2700"
  set plum "0.8667 0.6275 0.8667"
  set purple "0.6275 0.1255 0.9412"
  set purple_medium "0.5765 0.4392 0.8588"
  set ultramarine_violet "0.3600 0.1400 0.4300"
  set violet "0.5600 0.3700 0.6000"
  set violet_dark "0.5804 0.0000 0.8275"
  set violet_red "0.8157 0.1255 0.5647"
  set violet_red_medium "0.7804 0.0824 0.5216"
  set violet_red_pale "0.8588 0.4392 0.5765"

  }
  uplevel #0 eval $text
}

# **** ONLY USED FOR VTK VERSIONS < 4 ****
#
# Verbatim copy of vtkInt.tcl.
#
proc initialize_vtk_internal {} {
  set text {

  # a generic interactor for tcl and vtk
  #
  catch {unset vtkInteract.bold}
  catch {unset vtkInteract.normal}
  catch {unset vtkInteract.tagcount}
  set vtkInteractBold "-background #43ce80 -foreground #221133 -relief raised -borderwidth 1"
  set vtkInteractNormal "-background #dddddd -foreground #221133 -relief flat"
  set vtkInteractTagcount 1
  set vtkInteractCommandList ""
  set vtkInteractCommandIndex 0

  proc vtkInteract {} {
      global vtkInteractCommandList vtkInteractCommandIndex
      global vtkInteractTagcount

      proc dovtk {s w} {
	  global vtkInteractBold vtkInteractNormal vtkInteractTagcount 
	  global vtkInteractCommandList vtkInteractCommandIndex

	  set tag [append tagnum $vtkInteractTagcount]
	  set vtkInteractCommandIndex $vtkInteractTagcount
	  incr vtkInteractTagcount 1
	  .vtkInteract.display.text configure -state normal
	  .vtkInteract.display.text insert end $s $tag
	  set vtkInteractCommandList [linsert $vtkInteractCommandList end $s]
	  eval .vtkInteract.display.text tag configure $tag $vtkInteractNormal
	  .vtkInteract.display.text tag bind $tag <Any-Enter> \
	      ".vtkInteract.display.text tag configure $tag $vtkInteractBold"
	  .vtkInteract.display.text tag bind $tag <Any-Leave> \
	      ".vtkInteract.display.text tag configure $tag $vtkInteractNormal"
	  .vtkInteract.display.text tag bind $tag <1> "dovtk [list $s] .vtkInteract"
	  .vtkInteract.display.text insert end \n;
	  .vtkInteract.display.text insert end [uplevel 1 $s]
	  .vtkInteract.display.text insert end \n\n
	  .vtkInteract.display.text configure -state disabled
	  .vtkInteract.display.text yview end
      }

      catch {destroy .vtkInteract}
      toplevel .vtkInteract -bg #bbbbbb
      wm title .vtkInteract "vtk Interactor"
      wm iconname .vtkInteract "vtk"
      
      frame .vtkInteract.buttons -bg #bbbbbb
      pack  .vtkInteract.buttons -side bottom -fill both -expand 0 -pady 2m
      button .vtkInteract.buttons.dismiss -text Dismiss \
	  -command "wm withdraw .vtkInteract" \
	  -bg #bbbbbb -fg #221133 -activebackground #cccccc -activeforeground #221133
      pack .vtkInteract.buttons.dismiss -side left -expand 1 -fill x
      
      frame .vtkInteract.file -bg #bbbbbb
      label .vtkInteract.file.label -text "Command:" -width 10 -anchor w \
	  -bg #bbbbbb -fg #221133
      entry .vtkInteract.file.entry -width 40 \
	  -bg #dddddd -fg #221133 -highlightthickness 1 -highlightcolor #221133
      bind .vtkInteract.file.entry <Return> {
	  dovtk [%W get] .vtkInteract; %W delete 0 end}
      pack .vtkInteract.file.label -side left
      pack .vtkInteract.file.entry -side left -expand 1 -fill x
      
      frame .vtkInteract.display -bg #bbbbbb
      text .vtkInteract.display.text -yscrollcommand ".vtkInteract.display.scroll set" \
	  -setgrid true -width 60 -height 8 -wrap word -bg #dddddd -fg #331144 \
	  -state disabled
      scrollbar .vtkInteract.display.scroll \
	  -command ".vtkInteract.display.text yview" -bg #bbbbbb \
	  -troughcolor #bbbbbb -activebackground #cccccc -highlightthickness 0 
      pack .vtkInteract.display.text -side left -expand 1 -fill both
      pack .vtkInteract.display.scroll -side left -expand 0 -fill y

      pack .vtkInteract.display -side bottom -expand 1 -fill both
      pack .vtkInteract.file -pady 3m -padx 2m -side bottom -fill x 

      set vtkInteractCommandIndex 0
      
      bind .vtkInteract <Down> {
	if { $vtkInteractCommandIndex < [expr $vtkInteractTagcount - 1] } {
	  incr vtkInteractCommandIndex
	  set command_string [lindex $vtkInteractCommandList $vtkInteractCommandIndex]
	  .vtkInteract.file.entry delete 0 end
	  .vtkInteract.file.entry insert end $command_string
	} elseif { $vtkInteractCommandIndex == [expr $vtkInteractTagcount - 1] } {
	  .vtkInteract.file.entry delete 0 end
	}
      }

      bind .vtkInteract <Up> {
	if { $vtkInteractCommandIndex > 0 } { 
	  set vtkInteractCommandIndex [expr $vtkInteractCommandIndex - 1]
	  set command_string [lindex $vtkInteractCommandList $vtkInteractCommandIndex]
	  .vtkInteract.file.entry delete 0 end
	  .vtkInteract.file.entry insert end $command_string
	}
      }

      wm withdraw .vtkInteract
  }

  vtkInteract

  }
  uplevel #0 eval $text
}

# **** ONLY USED FOR VTK VERSIONS < 4 ****
#
# Verbatim copy of WidgetObject.tcl.
#
proc initialize_vtk_widget {} {
  set text {

  # These procs allow widgets to behave like objects with their own
  # state variables of processing objects.


  # generate a "unique" name for a widget variable
  proc GetWidgetVariable {widget varName} {
     regsub -all {\.} $widget "_" base

     return "$varName$base"
  }

  # returns an object which will be associated with a widget
  # A convienience method that creates a name for you  
  # based on the widget name and varible value/
  proc NewWidgetObject {widget type varName} {
     set var "[GetWidgetVariable $widget $varName]_Object"
     # create the vtk object
     $type $var

     # It is better to keep interface consistent
     # setting objects as variable values, and NewWidgetObject.
     SetWidgetVariableValue $widget $varName $var

     return $var
  }

  # obsolete!!!!!!!
  # returns the same thing as GetWidgetVariableValue
  proc GetWidgetObject {widget varName} {
     puts "Warning: obsolete call: GetWidgetObject"
     puts "Please use GetWidgetVariableValue"
     return "[GetWidgetVariable $widget $varName]_Object"
  }

  # sets the value of a widget variable
  proc SetWidgetVariableValue {widget varName value} {
     set var [GetWidgetVariable $widget $varName]
     global $var
     set $var $value
  }

  # This proc has alway eluded me.
  proc GetWidgetVariableValue {widget varName} {
     set var [GetWidgetVariable $widget $varName]
     global $var
     set temp ""
     catch {eval "set temp [format {$%s} $var]"}

     return $temp
  }

  }
  uplevel #0 eval $text
}

# **** ONLY USED FOR VTK VERSIONS < 4 ****
#
# Verbatim copy of TkInteractor.tcl, except for the changes marked with
# CHANGE START/END block.
#
proc initialize_vtk_interactor {} {
  set text {

  ## Procedure should be called to set bindings and initialize variables
  #

  ## MK CHANGE START -- comment out from here to END of CHANGE.
  if {0} {
  catch {source ../../examplesTcl/vtkInt.tcl}
  if { [catch {set VTK_TCL $env(VTK_TCL)}] != 0} { set VTK_TCL "../../examplesTcl" }
  if { [catch {set VTK_DATA $env(VTK_DATA)}] != 0} { set VTK_DATA "$VTK_DATA" }

  catch {source $VTK_TCL/WidgetObject.tcl}
  } else {
    initialize_vtk_internal
    initialize_vtk_widget
  }
  ## MK CHANGE END

  set TkInteractor_StartRenderMethod ""
  set TkInteractor_EndRenderMethod ""
  set TkInteractor_InteractiveUpdateRate 15.0
  set TkInteractor_StillUpdateRate 0.1

  proc BindTkRenderWidget {widget} {
      bind $widget <Any-ButtonPress> {StartMotion %W %x %y}
      bind $widget <Any-ButtonRelease> {EndMotion %W %x %y}
      bind $widget <B1-Motion> {Rotate %W %x %y}
      bind $widget <B2-Motion> {Pan %W %x %y}
      bind $widget <B3-Motion> {Zoom %W %x %y}
      bind $widget <Shift-B1-Motion> {Pan %W %x %y}
      bind $widget <Shift-B3-Motion> {RubberZoom %W %x %y}
      bind $widget <KeyPress-r> {Reset %W %x %y}
      bind $widget <KeyPress-u> {wm deiconify .vtkInteract}
      bind $widget <KeyPress-w> {Wireframe %W}
      bind $widget <KeyPress-s> {Surface %W}
      bind $widget <KeyPress-p> {PickActor %W %x %y}
      bind $widget <Enter> {Enter %W %x %y}
      bind $widget <Leave> {focus $oldFocus}
      bind $widget <Expose> {Expose %W}
  }

  # a litle more complex than just "bind $widget <Expose> {%W Render}"
  # we have to handle all pending expose events otherwise they que up.
  proc Expose {widget} {
      global TkInteractor_StillUpdateRate
      if {[GetWidgetVariableValue $widget InExpose] == 1} {
	  return
      }
      SetWidgetVariableValue $widget InExpose 1    
      [$widget GetRenderWindow] SetDesiredUpdateRate $TkInteractor_StillUpdateRate
      update
      [$widget GetRenderWindow] Render
      SetWidgetVariableValue $widget InExpose 0
  }

  # Global variable keeps track of whether active renderer was found
  set RendererFound 0

  # Create event bindings
  #
  proc Render {widget} {
      global CurrentCamera CurrentLight
      global TkInteractor_StartRenderMethod
      global TkInteractor_EndRenderMethod

      if { $TkInteractor_StartRenderMethod != "" } {
	  $TkInteractor_StartRenderMethod
      }

      eval $CurrentLight SetPosition [$CurrentCamera GetPosition]
      eval $CurrentLight SetFocalPoint [$CurrentCamera GetFocalPoint]

      $widget Render

      if { $TkInteractor_EndRenderMethod != "" } {
	  $TkInteractor_EndRenderMethod
      }
  }

  proc UpdateRenderer {widget x y} {
      global CurrentCamera CurrentLight 
      global CurrentRenderWindow CurrentRenderer
      global RendererFound LastX LastY
      global WindowCenterX WindowCenterY

      # Get the renderer window dimensions
      set WindowX [lindex [$widget configure -width] 4]
      set WindowY [lindex [$widget configure -height] 4]

      # Find which renderer event has occurred in
      set CurrentRenderWindow [$widget GetRenderWindow]
      set renderers [$CurrentRenderWindow GetRenderers]
      set numRenderers [$renderers GetNumberOfItems]

      $renderers InitTraversal; set RendererFound 0
      for {set i 0} {$i < $numRenderers} {incr i} {
	  set CurrentRenderer [$renderers GetNextItem]
	  set vx [expr double($x) / $WindowX]
	  set vy [expr ($WindowY - double($y)) / $WindowY]
	  set viewport [$CurrentRenderer GetViewport]
	  set vpxmin [lindex $viewport 0]
	  set vpymin [lindex $viewport 1]
	  set vpxmax [lindex $viewport 2]
	  set vpymax [lindex $viewport 3]
	  if { $vx >= $vpxmin && $vx <= $vpxmax && \
	  $vy >= $vpymin && $vy <= $vpymax} {
	      set RendererFound 1
	      set WindowCenterX [expr double($WindowX)*(($vpxmax - $vpxmin)/2.0\
				  + $vpxmin)]
	      set WindowCenterY [expr double($WindowY)*(($vpymax - $vpymin)/2.0\
				  + $vpymin)]
	      break
	  }
      }
      
      set CurrentCamera [$CurrentRenderer GetActiveCamera]
      set lights [$CurrentRenderer GetLights]
      $lights InitTraversal; set CurrentLight [$lights GetNextItem]
     
      set LastX $x
      set LastY $y
  }

  proc Enter {widget x y} {
      global oldFocus

      set oldFocus [focus]
      focus $widget
      UpdateRenderer $widget $x $y
  }

  proc StartMotion {widget x y} {
      global CurrentCamera CurrentLight 
      global CurrentRenderWindow CurrentRenderer
      global LastX LastY
      global RendererFound
      global TkInteractor_InteractiveUpdateRate
      global RubberZoomPerformed

      UpdateRenderer $widget $x $y
      if { ! $RendererFound } { return }

      set RubberZoomPerformed 0

      $CurrentRenderWindow SetDesiredUpdateRate $TkInteractor_InteractiveUpdateRate
  }

  proc EndMotion {widget x y} {
      global CurrentRenderWindow
      global RendererFound
      global TkInteractor_StillUpdateRate
      global RubberZoomPerformed
      global CurrentRenderer

      if { ! $RendererFound } {return}
      $CurrentRenderWindow SetDesiredUpdateRate $TkInteractor_StillUpdateRate


      if { $RubberZoomPerformed } {
	  #
	  # CHECK/UPDATE: MK CHANGE
	  # vtkRenderer in VTK 2.2 and earlier didn't support 
	  # RemoveProp, so handle it here.
	  #
	  if {[lsearch [$CurrentRenderer ListMethods] RemoveProp] != -1} {
	    $CurrentRenderer RemoveProp RubberBandActor
	  }
	  #
	  # END MK CHANGE.
	  #
	  DoRubberZoom $widget
      }

      Render $widget
  }

  # Objects used to display rubberband
  vtkPoints            RubberBandPoints
  vtkCellArray         RubberBandLines
  vtkScalars           RubberBandScalars
  vtkPolyData          RubberBandPolyData
  vtkPolyDataMapper2D  RubberBandMapper
  vtkActor2D           RubberBandActor
  vtkLookupTable       RubberBandColors

  RubberBandPolyData SetPoints      RubberBandPoints
  RubberBandPolyData SetLines       RubberBandLines
  RubberBandMapper   SetInput       RubberBandPolyData
  RubberBandMapper   SetLookupTable RubberBandColors
  RubberBandActor    SetMapper      RubberBandMapper

  RubberBandColors SetNumberOfTableValues 2
  RubberBandColors SetNumberOfColors 2
  RubberBandColors SetTableValue 0 1.0 0.0 0.0 1.0
  RubberBandColors SetTableValue 1 1.0 1.0 1.0 1.0

  [RubberBandPolyData GetPointData] SetScalars RubberBandScalars

  RubberBandMapper SetScalarRange 0 1

  RubberBandPoints InsertPoint 0  0  0  0
  RubberBandPoints InsertPoint 1  0 10  0
  RubberBandPoints InsertPoint 2 10 10  0
  RubberBandPoints InsertPoint 3 10  0  0

  RubberBandLines  InsertNextCell 5
  RubberBandLines  InsertCellPoint 0
  RubberBandLines  InsertCellPoint 1
  RubberBandLines  InsertCellPoint 2
  RubberBandLines  InsertCellPoint 3
  RubberBandLines  InsertCellPoint 0

  RubberBandScalars InsertNextScalar 0
  RubberBandScalars InsertNextScalar 1
  RubberBandScalars InsertNextScalar 0
  RubberBandScalars InsertNextScalar 1

  RubberBandMapper ScalarVisibilityOn

  # Called when the mouse button is release - do the zoom
  proc DoRubberZoom { widget } {
      global CurrentCamera CurrentRenderer
      global RendererFound
      global StartRubberZoomX StartRubberZoomY
      global EndRubberZoomX EndRubberZoomY

      # Return if there is no renderer, or the rubber band is less
      # that 5 pixels in either direction
      if { ! $RendererFound } { return }
      if { [expr $StartRubberZoomX - $EndRubberZoomX] < 5 && \
	      [expr $StartRubberZoomX - $EndRubberZoomX] > -5 } { return }
      if { [expr $StartRubberZoomY - $EndRubberZoomY] < 5 && \
	      [expr $StartRubberZoomY - $EndRubberZoomY] > -5 } { return }
      
      # We'll need the window height later
      set WindowY [lindex [$widget configure -height] 4]

      # What is the center of the rubber band box in pixels?
      set centerX [expr ($StartRubberZoomX + $EndRubberZoomX)/2.0]
      set centerY [expr ($StartRubberZoomY + $EndRubberZoomY)/2.0]

      # Convert the focal point to a display coordinate in order to get the
      # depth of the focal point in display units
      set FPoint [$CurrentCamera GetFocalPoint]
	  set FPoint0 [lindex $FPoint 0]
	  set FPoint1 [lindex $FPoint 1]
	  set FPoint2 [lindex $FPoint 2]
      $CurrentRenderer SetWorldPoint $FPoint0 $FPoint1 $FPoint2 1.0
      $CurrentRenderer WorldToDisplay
      set DPoint [$CurrentRenderer GetDisplayPoint]
      set focalDepth [lindex $DPoint 2]

      # Convert the position of the camera to a display coordinate in order
      # to get the depth of the camera in display coordinates. Note this is
      # a negative number (behind the near clipping plane of 0) but it works
      # ok anyway
      set PPoint [$CurrentCamera GetPosition]
	  set PPoint0 [lindex $PPoint 0]
	  set PPoint1 [lindex $PPoint 1]
	  set PPoint2 [lindex $PPoint 2]
      $CurrentRenderer SetWorldPoint $PPoint0 $PPoint1 $PPoint2 1.0
      $CurrentRenderer WorldToDisplay
      set DPoint [$CurrentRenderer GetDisplayPoint]
      set positionDepth [lindex $DPoint 2]

      # Find out the world position of where our new focal point should
      # be - it will be at the center of the box, back at the same focal depth
      # Don't actually set it now - we need to do all our computations before
      # we modify the camera
      $CurrentRenderer SetDisplayPoint $centerX $centerY $focalDepth
      $CurrentRenderer DisplayToWorld
      set newFocalPoint [$CurrentRenderer GetWorldPoint]
      set newFocalPoint0 [lindex $newFocalPoint 0]
      set newFocalPoint1 [lindex $newFocalPoint 1]
      set newFocalPoint2 [lindex $newFocalPoint 2]
      set newFocalPoint3 [lindex $newFocalPoint 3]
      if { $newFocalPoint3 != 0.0 } {
	  set newFocalPoint0 [expr $newFocalPoint0 / $newFocalPoint3]
	  set newFocalPoint1 [expr $newFocalPoint1 / $newFocalPoint3]
	  set newFocalPoint2 [expr $newFocalPoint2 / $newFocalPoint3]
      }

      # Find out where the new camera position will be - at the center of
      # the rubber band box at the position depth. Don't set it yet...
      $CurrentRenderer SetDisplayPoint $centerX $centerY $positionDepth
      $CurrentRenderer DisplayToWorld
      set newPosition [$CurrentRenderer GetWorldPoint]
      set newPosition0 [lindex $newPosition 0]
      set newPosition1 [lindex $newPosition 1]
      set newPosition2 [lindex $newPosition 2]
      set newPosition3 [lindex $newPosition 3]
      if { $newPosition3 != 0.0 } {
	  set newPosition0 [expr $newPosition0 / $newPosition3]
	  set newPosition1 [expr $newPosition1 / $newPosition3]
	  set newPosition2 [expr $newPosition2 / $newPosition3]
      }

      # We figured out how to position the camera to be centered, now we
      # need to "zoom". In parallel, this is simple since we only need to
      # change our parallel scale to encompass the entire y range of the
      # rubber band box. In perspective, we assume the box is drawn on the
      # near plane - this means that it is not possible that someone can
      # draw a rubber band box around a nearby object and dolly past it. It 
      # also means that you won't get very close to distance objects - but that
      # seems better than getting lost.
      if {[$CurrentCamera GetParallelProjection]} {
	  # the new scale is just based on the y size of the rubber band box
	  # compared to the y size of the window
	  set ydiff [expr ($StartRubberZoomX - $EndRubberZoomX)]
	  if { $ydiff < 0.0 } { set ydiff [expr $ydiff * -1.0] }
	  set newScale [$CurrentCamera GetParallelScale]
	  set newScale [expr $newScale * $ydiff / $WindowY]

	  # now we can actually modify the camera
	  $CurrentCamera SetFocalPoint $newFocalPoint0 $newFocalPoint1 $newFocalPoint2
	  $CurrentCamera SetPosition $newPosition0 $newPosition1 $newPosition2
	  $CurrentCamera SetParallelScale $newScale

      } else {
	  # find out the center of the rubber band box on the near plane
	  $CurrentRenderer SetDisplayPoint $centerX $centerY 0.0
	  $CurrentRenderer DisplayToWorld
	  set nearFocalPoint [$CurrentRenderer GetWorldPoint]
	  set nearFocalPoint0 [lindex $nearFocalPoint 0]
	  set nearFocalPoint1 [lindex $nearFocalPoint 1]
	  set nearFocalPoint2 [lindex $nearFocalPoint 2]
	  set nearFocalPoint3 [lindex $nearFocalPoint 3]
	  if { $nearFocalPoint3 != 0.0 } {
	      set nearFocalPoint0 [expr $nearFocalPoint0 / $nearFocalPoint3]
	      set nearFocalPoint1 [expr $nearFocalPoint1 / $nearFocalPoint3]
	      set nearFocalPoint2 [expr $nearFocalPoint2 / $nearFocalPoint3]
	  }

	  # find the world coordinates of the point centered on the rubber band box
	  # in x, on the border in y, and at the near plane depth.
	  $CurrentRenderer SetDisplayPoint $centerX $StartRubberZoomY  0.0
	  $CurrentRenderer DisplayToWorld
	  set focalEdge [$CurrentRenderer GetWorldPoint]
	  set focalEdge0 [lindex $focalEdge 0]
	  set focalEdge1 [lindex $focalEdge 1]
	  set focalEdge2 [lindex $focalEdge 2]
	  set focalEdge3 [lindex $focalEdge 3]
	  if { $focalEdge3 != 0.0 } {
	      set focalEdge0 [expr $focalEdge0 / $focalEdge3]
	      set focalEdge1 [expr $focalEdge1 / $focalEdge3]
	      set focalEdge2 [expr $focalEdge2 / $focalEdge3]
	  }

	  # how far is this "rubberband edge point" from the focal point?
	  set ydist [expr \
		  sqrt( \
		  ($nearFocalPoint0 - $focalEdge0)*($nearFocalPoint0 - $focalEdge0) + \
		  ($nearFocalPoint1 - $focalEdge1)*($nearFocalPoint1 - $focalEdge1) + \
		  ($nearFocalPoint2 - $focalEdge2)*($nearFocalPoint2 - $focalEdge2) )]

	  # We need to know how far back we must be so that when we view the scene
	  # with the current view angle, we see all of the y range of the rubber
	  # band box. Use a simple tangent equation - opposite / adjacent = tan theta
	  # where opposite is half the y height of the rubber band box on the near
	  # plane, adjacent is the distance we are solving for, and theta is half
	  # the viewing angle. This distance that we solve for is the new distance
	  # to the near plane - to find the new distance to the focal plane we
	  # must take the old distance to the focal plane, subtract the near plane
	  # distance, and add in the distance we solved for.
	  set angle [expr 0.5 * (3.141592 / 180.0) * [$CurrentCamera GetViewAngle]]
	  set d [expr $ydist/tan($angle)]
	  set range [$CurrentCamera GetClippingRange]
	  set nearplane [lindex $range 0]
	  set factor [expr [$CurrentCamera GetDistance] / \
		  ([$CurrentCamera GetDistance] - $nearplane + $d)]

	  # now we can actually modify the camera
	  $CurrentCamera SetFocalPoint $newFocalPoint0 $newFocalPoint1 $newFocalPoint2
	  $CurrentCamera SetPosition $newPosition0 $newPosition1 $newPosition2
	  #
	  # CHECK/UPDATE: MK CHANGE
	  # vtkRenderer in VTK 2.2 and earlier didn't support 
	  # ResetCameraClippingRange, so handle it here.
	  #
	  if {[lsearch [$CurrentRenderer ListMethods] \
	                ResetCameraClippingRange] == -1} {
	    set clippingRange [$CurrentCamera GetClippingRange]
	    set minRange [lindex $clippingRange 0]
	    set maxRange [lindex $clippingRange 1]
	    $CurrentCamera SetClippingRange [expr $minRange / $factor] \
					    [expr $maxRange / $factor]
	    $CurrentCamera Dolly $factor
	  } else {
	    $CurrentCamera Dolly $factor
	    $CurrentRenderer ResetCameraClippingRange
	  }
	  #
	  # END MK CHANGE.
	  #
      }    
  }

  proc Rotate {widget x y} {
      global CurrentCamera 
      global LastX LastY
      global RendererFound

      if { ! $RendererFound } { return }

      $CurrentCamera Azimuth [expr ($LastX - $x)]
      $CurrentCamera Elevation [expr ($y - $LastY)]
      $CurrentCamera OrthogonalizeViewUp

      set LastX $x
      set LastY $y

      Render $widget
  }

  proc RubberZoom {widget x y} {
      global RendererFound
      global CurrentRenderer
      global RubberZoomPerformed
      global LastX LastY
      global StartRubberZoomX StartRubberZoomY
      global EndRubberZoomX EndRubberZoomY

      if { ! $RendererFound } { return }

      set WindowY [lindex [$widget configure -height] 4]

      if { ! $RubberZoomPerformed } {
	  #
	  # CHECK/UPDATE: MK CHANGE
	  # vtkRenderer in VTK 2.2 and earlier didn't support 
	  # AddProp, so handle it here.
	  #
	  if {[lsearch [$CurrentRenderer ListMethods] AddProp] != -1} {
	    $CurrentRenderer AddProp RubberBandActor
	  }
	  #
	  # END MK CHANGE.
	  #

	  set StartRubberZoomX $x
	  set StartRubberZoomY [expr $WindowY - $y - 1]
	  
	  set RubberZoomPerformed 1
      }

      set EndRubberZoomX $x
      set EndRubberZoomY [expr $WindowY - $y - 1]

      RubberBandPoints SetPoint 0 $StartRubberZoomX $StartRubberZoomY  0
      RubberBandPoints SetPoint 1 $StartRubberZoomX $EndRubberZoomY    0
      RubberBandPoints SetPoint 2 $EndRubberZoomX   $EndRubberZoomY    0
      RubberBandPoints SetPoint 3 $EndRubberZoomX   $StartRubberZoomY  0

      Render $widget
  }


  proc Pan {widget x y} {
      global CurrentRenderer CurrentCamera
      global WindowCenterX WindowCenterY LastX LastY
      global RendererFound

      if { ! $RendererFound } { return }

      set FPoint [$CurrentCamera GetFocalPoint]
	  set FPoint0 [lindex $FPoint 0]
	  set FPoint1 [lindex $FPoint 1]
	  set FPoint2 [lindex $FPoint 2]

      set PPoint [$CurrentCamera GetPosition]
	  set PPoint0 [lindex $PPoint 0]
	  set PPoint1 [lindex $PPoint 1]
	  set PPoint2 [lindex $PPoint 2]

      $CurrentRenderer SetWorldPoint $FPoint0 $FPoint1 $FPoint2 1.0
      $CurrentRenderer WorldToDisplay
      set DPoint [$CurrentRenderer GetDisplayPoint]
      set focalDepth [lindex $DPoint 2]

      set APoint0 [expr $WindowCenterX + ($x - $LastX)]
      set APoint1 [expr $WindowCenterY - ($y - $LastY)]

      $CurrentRenderer SetDisplayPoint $APoint0 $APoint1 $focalDepth
      $CurrentRenderer DisplayToWorld
      set RPoint [$CurrentRenderer GetWorldPoint]
	  set RPoint0 [lindex $RPoint 0]
	  set RPoint1 [lindex $RPoint 1]
	  set RPoint2 [lindex $RPoint 2]
	  set RPoint3 [lindex $RPoint 3]
      if { $RPoint3 != 0.0 } {
	  set RPoint0 [expr $RPoint0 / $RPoint3]
	  set RPoint1 [expr $RPoint1 / $RPoint3]
	  set RPoint2 [expr $RPoint2 / $RPoint3]
      }

      $CurrentCamera SetFocalPoint \
	[expr ($FPoint0 - $RPoint0)/2.0 + $FPoint0] \
	[expr ($FPoint1 - $RPoint1)/2.0 + $FPoint1] \
	[expr ($FPoint2 - $RPoint2)/2.0 + $FPoint2]

      $CurrentCamera SetPosition \
	[expr ($FPoint0 - $RPoint0)/2.0 + $PPoint0] \
	[expr ($FPoint1 - $RPoint1)/2.0 + $PPoint1] \
	[expr ($FPoint2 - $RPoint2)/2.0 + $PPoint2]

      set LastX $x
      set LastY $y

      Render $widget
  }

  proc Zoom {widget x y} {
      global CurrentCamera CurrentRenderer
      global LastX LastY
      global RendererFound

      if { ! $RendererFound } { return }

      set zoomFactor [expr pow(1.02,(0.5*($y - $LastY)))]

      if {[$CurrentCamera GetParallelProjection]} {
	  set parallelScale [expr [$CurrentCamera GetParallelScale] * $zoomFactor];
	  $CurrentCamera SetParallelScale $parallelScale;
      } else {
	  #
	  # CHECK/UPDATE: MK CHANGE
	  # vtkRenderer in VTK 2.2 and earlier didn't support 
	  # ResetCameraClippingRange, so handle it here.
	  #
	  if {[lsearch [$CurrentRenderer ListMethods] \
	                ResetCameraClippingRange] == -1} {
	    set clippingRange [$CurrentCamera GetClippingRange]
	    set minRange [lindex $clippingRange 0]
	    set maxRange [lindex $clippingRange 1]
	    $CurrentCamera SetClippingRange [expr $minRange / $zoomFactor] \
					    [expr $maxRange / $zoomFactor]
	    $CurrentCamera Dolly $zoomFactor
	  } else {
	    $CurrentCamera Dolly $zoomFactor
	    $CurrentRenderer ResetCameraClippingRange
	  }
	  #
	  # END MK CHANGE.
	  #
      }

      set LastX $x
      set LastY $y

      Render $widget
  }

  proc Reset {widget x y} {
      global CurrentRenderWindow
      global RendererFound
      global CurrentRenderer

      # Get the renderer window dimensions
      set WindowX [lindex [$widget configure -width] 4]
      set WindowY [lindex [$widget configure -height] 4]

      # Find which renderer event has occurred in
      set CurrentRenderWindow [$widget GetRenderWindow]
      set renderers [$CurrentRenderWindow GetRenderers]
      set numRenderers [$renderers GetNumberOfItems]

      $renderers InitTraversal; set RendererFound 0
      for {set i 0} {$i < $numRenderers} {incr i} {
	  set CurrentRenderer [$renderers GetNextItem]
	  set vx [expr double($x) / $WindowX]
	  set vy [expr ($WindowY - double($y)) / $WindowY]

	  set viewport [$CurrentRenderer GetViewport]
	  set vpxmin [lindex $viewport 0]
	  set vpymin [lindex $viewport 1]
	  set vpxmax [lindex $viewport 2]
	  set vpymax [lindex $viewport 3]
	  if { $vx >= $vpxmin && $vx <= $vpxmax && \
	  $vy >= $vpymin && $vy <= $vpymax} {
	      set RendererFound 1
	      break
	  }
      }

      if { $RendererFound } {$CurrentRenderer ResetCamera}

      Render $widget
  }

  proc Wireframe {widget} {
      global CurrentRenderer

      set actors [$CurrentRenderer GetActors]

      $actors InitTraversal
      set actor [$actors GetNextItem]
      while { $actor != "" } {
	  [$actor GetProperty] SetRepresentationToWireframe
	  set actor [$actors GetNextItem]
      }

      Render $widget
  }

  proc Surface {widget} {
      global CurrentRenderer

      set actors [$CurrentRenderer GetActors]

      $actors InitTraversal
      set actor [$actors GetNextItem]
      while { $actor != "" } {
	  [$actor GetProperty] SetRepresentationToSurface
	  set actor [$actors GetNextItem]
      }

      Render $widget
  }

  # Used to support picking operations
  #
  set PickedAssembly ""
  vtkCellPicker ActorPicker
  vtkProperty PickedProperty
      PickedProperty SetColor 1 0 0
  set PrePickedProperty ""

  proc PickActor {widget x y} {
      global CurrentRenderer RendererFound
      global PickedAssembly PrePickedProperty WindowY

      set WindowY [lindex [$widget configure -height] 4]

      if { ! $RendererFound } { return }
      ActorPicker Pick $x [expr $WindowY - $y - 1] 0.0 $CurrentRenderer
      set assembly [ActorPicker GetAssembly]

      if { $PickedAssembly != "" && $PrePickedProperty != "" } {
	  $PickedAssembly SetProperty $PrePickedProperty
	  # release hold on the property
	  $PrePickedProperty UnRegister $PrePickedProperty
	  set PrePickedProperty ""
      }

      if { $assembly != "" } {
	  set PickedAssembly $assembly
	  set PrePickedProperty [$PickedAssembly GetProperty]
	  # hold onto the property
	  $PrePickedProperty Register $PrePickedProperty
	  $PickedAssembly SetProperty PickedProperty
      }

      Render $widget
  }

  }
  uplevel #0 eval $text
}

#
# End VTK Internal Tcl Files
#
#### 

##
## START MAIN PROGRAM.
##

run_main 

# TESTING CODE.
if {0} {
  load_file layout.dat
  update
  file delete -force foo.eps
  export_image EPS foo.eps
  file delete -force foo.jpg
  export_image JPEG foo.jpg
  file delete -force foo.tif
  export_image TIFF foo.tif
  file delete -force foo.ppm
  export_image PPM foo.ppm
  update
}

