source: trunk/gui/scripts/vtkimageviewer.tcl @ 6356

Last change on this file since 6356 was 6356, checked in by ldelgass, 8 years ago

apply fix for stretch to fit from heightmap in r5914 to image viewer

File size: 77.9 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: vtkimageviewer - Vtk image viewer
4#
5#  It connects to the Vtkvis server running on a rendering farm,
6#  transmits data, and displays the results.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2016  HUBzero Foundation, LLC
10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13# ======================================================================
14package require Itk
15package require BLT
16#package require Img
17
18option add *VtkImageViewer.width 4i widgetDefault
19option add *VtkImageViewer*cursor crosshair widgetDefault
20option add *VtkImageViewer.height 4i widgetDefault
21option add *VtkImageViewer.foreground black widgetDefault
22option add *VtkImageViewer.controlBackground gray widgetDefault
23option add *VtkImageViewer.controlDarkBackground #999999 widgetDefault
24option add *VtkImageViewer.plotBackground black widgetDefault
25option add *VtkImageViewer.plotForeground white widgetDefault
26option add *VtkImageViewer.font \
27    -*-helvetica-medium-r-normal-*-12-* widgetDefault
28
29# must use this name -- plugs into Rappture::resources::load
30proc VtkImageViewer_init_resources {} {
31    Rappture::resources::register \
32        vtkvis_server Rappture::VtkImageViewer::SetServerList
33}
34
35itcl::class Rappture::VtkImageViewer {
36    inherit Rappture::VisViewer
37
38    itk_option define -plotforeground plotForeground Foreground ""
39    itk_option define -plotbackground plotBackground Background ""
40    itk_option define -mode mode Mode "vtkimage"
41
42    constructor { args } {
43        Rappture::VisViewer::constructor
44    } {
45        # defined below
46    }
47    destructor {
48        # defined below
49    }
50    public proc SetServerList { namelist } {
51        Rappture::VisViewer::SetServerList "vtkvis" $namelist
52    }
53    public method add {dataobj {settings ""}}
54    public method camera {option args}
55    public method delete {args}
56    public method disconnect {}
57    public method download {option args}
58    public method get {args}
59    public method isconnected {}
60    public method parameters {title args} {
61        # do nothing
62    }
63    public method scale {args}
64
65    # The following methods are only used by this class.
66    private method AdjustSetting {what {value ""}}
67    private method BuildAxisTab {}
68    private method BuildCameraTab {}
69    private method BuildColormap { name }
70    private method BuildImageTab {}
71    private method BuildDownloadPopup { widget command }
72    private method Connect {}
73    private method CurrentDatasets {args}
74    private method Disconnect {}
75    private method DoResize {}
76    private method DoRotate {}
77    private method DrawLegend {}
78    private method EnterLegend { x y }
79    private method EventuallyRequestLegend {}
80    private method EventuallyResize { w h }
81    private method EventuallyRotate { q }
82    private method GetImage { args }
83    private method GetVtkData { args }
84    private method InitSettings { args }
85    private method IsValidObject { dataobj }
86    private method LeaveLegend {}
87    private method LegendTitleAction { option }
88    private method MotionLegend { x y }
89    private method Pan {option x y}
90    private method PanCamera {}
91    private method Pick {x y}
92    private method QuaternionToView { q } {
93        foreach { _view(-qw) _view(-qx) _view(-qy) _view(-qz) } $q break
94    }
95    private method Rebuild {}
96    private method ReceiveDataset { args }
97    private method ReceiveImage { args }
98    private method ReceiveLegend { colormap title min max size }
99    private method RequestLegend {}
100    private method Rotate {option x y}
101    private method SetCurrentColormap { color }
102    private method SetCurrentFieldName { dataobj }
103    private method SetLegendTip { x y }
104    private method SetObjectStyle { dataobj comp }
105    private method SetOrientation { side }
106    private method ViewToQuaternion {} {
107        return [list $_view(-qw) $_view(-qx) $_view(-qy) $_view(-qz)]
108    }
109    private method Zoom {option}
110
111    private variable _arcball ""
112    private variable _dlist "";         # list of data objects
113    private variable _obj2ovride;       # maps dataobj => style override
114    private variable _datasets;         # contains all the dataobj-component
115                                        # datasets in the server
116    private variable _colormaps;        # contains all the colormaps
117                                        # in the server.
118
119    # The name of the current colormap used.  The colormap is global to all
120    # images displayed.
121    private variable _currentColormap ""
122    private variable _currentNumIsolines -1
123
124    private variable _maxScale 100;     # This is the # of times the x-axis
125                                        # and y-axis ranges can differ before
126                                        # automatically turning on
127                                        # -stretchtofit
128
129    private variable _click;            # info used for rotate operations
130    private variable _limits;           # Holds overall limits for all dataobjs
131                                        # using the viewer.
132    private variable _view;             # view params for 3D view
133    private variable _settings
134    private variable _changed
135    private variable _reset 1;          # Connection to server has been reset.
136
137    private variable _first "";         # This is the topmost dataset.
138    private variable _start 0
139    private variable _isolines
140    private variable _contourList ""
141    private variable _width 0
142    private variable _height 0
143    private variable _legendWidth 0
144    private variable _legendHeight 0
145    private variable _resizePending 0
146    private variable _legendPending 0
147    private variable _rotatePending 0
148    private variable _fields
149    private variable _curFldName ""
150    private variable _curFldLabel ""
151    private variable _colorMode "scalar"; # Mode of colormap (vmag or scalar)
152
153    private common _downloadPopup;      # download options from popup
154    private common _hardcopy
155}
156
157itk::usual VtkImageViewer {
158    keep -background -foreground -cursor -font
159    keep -plotbackground -plotforeground -mode
160}
161
162# ----------------------------------------------------------------------
163# CONSTRUCTOR
164# ----------------------------------------------------------------------
165itcl::body Rappture::VtkImageViewer::constructor {args} {
166    set _serverType "vtkvis"
167
168    #DebugOn
169    EnableWaitDialog 900
170
171    # Rebuild event
172    $_dispatcher register !rebuild
173    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
174
175    # Resize event
176    $_dispatcher register !resize
177    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
178
179    # Legend event
180    $_dispatcher register !legend
181    $_dispatcher dispatch $this !legend "[itcl::code $this RequestLegend]; list"
182
183    # Rotate event
184    $_dispatcher register !rotate
185    $_dispatcher dispatch $this !rotate "[itcl::code $this DoRotate]; list"
186
187    #
188    # Populate parser with commands handle incoming requests
189    #
190    $_parser alias image [itcl::code $this ReceiveImage]
191    $_parser alias legend [itcl::code $this ReceiveLegend]
192    $_parser alias dataset [itcl::code $this ReceiveDataset]
193
194    # Create image for legend colorbar.
195    set _image(legend) [image create photo]
196
197    # Initialize the view to some default parameters.
198    array set _view {
199        -ortho    0
200        -qw       0.36
201        -qx       0.25
202        -qy       0.50
203        -qz       0.70
204        -xpan     0
205        -ypan     0
206        -zoom     1.0
207    }
208    set _arcball [blt::arcball create 100 100]
209    $_arcball quaternion [ViewToQuaternion]
210
211    array set _settings {
212        -axesvisible            1
213        -axislabels             1
214        -axisminorticks         1
215        -axismode               "static"
216        -backingcolor           white
217        -backingvisible         1
218        -colormapdiscrete       0
219        -field                  "Default"
220        -legendvisible          0
221        -level                  127.5
222        -numcolors              256
223        -opacity                100
224        -outline                0
225        -saveopacity            100
226        -stretchtofit           0
227        -view3d                 0
228        -window                 255.0
229        -xgrid                  0
230        -ygrid                  0
231        -zgrid                  0
232    }
233    array set _changed {
234        -colormap               0
235        -opacity                0
236    }
237    itk_component add view {
238        canvas $itk_component(plotarea).view \
239            -highlightthickness 0 -borderwidth 0
240    } {
241        usual
242        ignore -highlightthickness -borderwidth -background
243    }
244
245    itk_component add fieldmenu {
246        menu $itk_component(plotarea).menu \
247            -relief flat \
248            -tearoff 0
249    } {
250        usual
251        ignore -background -foreground -relief -tearoff
252    }
253    set c $itk_component(view)
254    bind $c <Configure> [itcl::code $this EventuallyResize %w %h]
255    bind $c <Control-F1> [itcl::code $this ToggleConsole]
256
257    # Fix the scrollregion in case we go off screen
258    $c configure -scrollregion [$c bbox all]
259
260    set _map(id) [$c create image 0 0 -anchor nw -image $_image(plot)]
261    set _map(cwidth) -1
262    set _map(cheight) -1
263    set _map(zoom) 1.0
264    set _map(original) ""
265
266    set f [$itk_component(main) component controls]
267    itk_component add reset {
268        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
269            -highlightthickness 0 \
270            -image [Rappture::icon reset-view] \
271            -command [itcl::code $this Zoom reset]
272    } {
273        usual
274        ignore -highlightthickness
275    }
276    pack $itk_component(reset) -side top -padx 2 -pady { 2 0 }
277    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
278
279    itk_component add zoomin {
280        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
281            -highlightthickness 0 \
282            -image [Rappture::icon zoom-in] \
283            -command [itcl::code $this Zoom in]
284    } {
285        usual
286        ignore -highlightthickness
287    }
288    pack $itk_component(zoomin) -side top -padx 2 -pady { 2 0 }
289    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
290
291    itk_component add zoomout {
292        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
293            -highlightthickness 0 \
294            -image [Rappture::icon zoom-out] \
295            -command [itcl::code $this Zoom out]
296    } {
297        usual
298        ignore -highlightthickness
299    }
300    pack $itk_component(zoomout) -side top -padx 2 -pady { 2 0 }
301    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
302
303    itk_component add mode {
304        Rappture::PushButton $f.mode \
305            -onimage [Rappture::icon surface] \
306            -offimage [Rappture::icon surface] \
307            -variable [itcl::scope _settings(-view3d)] \
308            -command [itcl::code $this AdjustSetting -view3d] \
309    }
310    Rappture::Tooltip::for $itk_component(mode) \
311        "Toggle the surface/contour on/off"
312    pack $itk_component(mode) -padx 2 -pady { 2 0 }
313
314    itk_component add stretchtofit {
315        Rappture::PushButton $f.stretchtofit \
316            -onimage [Rappture::icon stretchtofit] \
317            -offimage [Rappture::icon stretchtofit] \
318            -variable [itcl::scope _settings(-stretchtofit)] \
319            -command [itcl::code $this AdjustSetting -stretchtofit] \
320    }
321    Rappture::Tooltip::for $itk_component(stretchtofit) \
322        "Stretch plot to fit window on/off"
323    pack $itk_component(stretchtofit) -padx 2 -pady 2
324
325    if { [catch {
326        BuildImageTab
327        BuildAxisTab
328        BuildCameraTab
329    } errs] != 0 } {
330        global errorInfo
331        puts stderr "errs=$errs errorInfo=$errorInfo"
332    }
333
334    # Hack around the Tk panewindow.  The problem is that the requested
335    # size of the 3d view isn't set until an image is retrieved from
336    # the server.  So the panewindow uses the tiny size.
337    set w 10000
338    pack forget $itk_component(view)
339    blt::table $itk_component(plotarea) \
340        0,0 $itk_component(view) -fill both -reqwidth $w
341    blt::table configure $itk_component(plotarea) c1 -resize none
342
343    # Bindings for panning via mouse
344    bind $itk_component(view) <ButtonPress-2> \
345        [itcl::code $this Pan click %x %y]
346    bind $itk_component(view) <B2-Motion> \
347        [itcl::code $this Pan drag %x %y]
348    bind $itk_component(view) <ButtonRelease-2> \
349        [itcl::code $this Pan release %x %y]
350
351    #bind $itk_component(view) <ButtonRelease-3> \
352    #    [itcl::code $this Pick %x %y]
353
354    # Bindings for panning via keyboard
355    bind $itk_component(view) <KeyPress-Left> \
356        [itcl::code $this Pan set -10 0]
357    bind $itk_component(view) <KeyPress-Right> \
358        [itcl::code $this Pan set 10 0]
359    bind $itk_component(view) <KeyPress-Up> \
360        [itcl::code $this Pan set 0 -10]
361    bind $itk_component(view) <KeyPress-Down> \
362        [itcl::code $this Pan set 0 10]
363    bind $itk_component(view) <Shift-KeyPress-Left> \
364        [itcl::code $this Pan set -2 0]
365    bind $itk_component(view) <Shift-KeyPress-Right> \
366        [itcl::code $this Pan set 2 0]
367    bind $itk_component(view) <Shift-KeyPress-Up> \
368        [itcl::code $this Pan set 0 -2]
369    bind $itk_component(view) <Shift-KeyPress-Down> \
370        [itcl::code $this Pan set 0 2]
371
372    # Bindings for zoom via keyboard
373    bind $itk_component(view) <KeyPress-Prior> \
374        [itcl::code $this Zoom out]
375    bind $itk_component(view) <KeyPress-Next> \
376        [itcl::code $this Zoom in]
377
378    bind $itk_component(view) <Enter> "focus $itk_component(view)"
379
380    if {[string equal "x11" [tk windowingsystem]]} {
381        # Bindings for zoom via mouse
382        bind $itk_component(view) <4> [itcl::code $this Zoom out]
383        bind $itk_component(view) <5> [itcl::code $this Zoom in]
384    }
385
386    set _image(download) [image create photo]
387
388    eval itk_initialize $args
389
390    Connect
391}
392
393# ----------------------------------------------------------------------
394# DESTRUCTOR
395# ----------------------------------------------------------------------
396itcl::body Rappture::VtkImageViewer::destructor {} {
397    Disconnect
398    image delete $_image(plot)
399    image delete $_image(download)
400    catch { blt::arcball destroy $_arcball }
401}
402
403itcl::body Rappture::VtkImageViewer::DoResize {} {
404    if { $_width < 2 } {
405        set _width 500
406    }
407    if { $_height < 2 } {
408        set _height 500
409    }
410    set _start [clock clicks -milliseconds]
411    SendCmd "screen size [expr $_width - 20] $_height"
412
413    set font "Arial 8"
414    set lh [font metrics $font -linespace]
415    set h [expr {$_height - 2 * ($lh + 2)}]
416    if { $h != $_legendHeight } {
417        EventuallyRequestLegend
418    } else {
419        DrawLegend
420    }
421    set _resizePending 0
422}
423
424itcl::body Rappture::VtkImageViewer::DoRotate {} {
425    SendCmd "camera orient [ViewToQuaternion]"
426    set _rotatePending 0
427}
428
429itcl::body Rappture::VtkImageViewer::EventuallyRequestLegend {} {
430    if { !$_legendPending } {
431        set _legendPending 1
432        $_dispatcher event -idle !legend
433    }
434}
435
436itcl::body Rappture::VtkImageViewer::EventuallyResize { w h } {
437    set _width $w
438    set _height $h
439    $_arcball resize $w $h
440    if { !$_resizePending } {
441        set _resizePending 1
442        $_dispatcher event -after 250 !resize
443    }
444}
445
446itcl::body Rappture::VtkImageViewer::EventuallyRotate { q } {
447    QuaternionToView $q
448    if { !$_rotatePending } {
449        set _rotatePending 1
450        $_dispatcher event -after 100 !rotate
451    }
452}
453
454# ----------------------------------------------------------------------
455# USAGE: add <dataobj> ?<settings>?
456#
457# Clients use this to add a data object to the plot.  The optional
458# <settings> are used to configure the plot.  Allowed settings are
459# -color, -brightness, -width, -linestyle, and -raise.
460# ----------------------------------------------------------------------
461itcl::body Rappture::VtkImageViewer::add {dataobj {settings ""}} {
462    if { ![$dataobj isvalid] } {
463        return;                         # Object doesn't contain valid data.
464    }
465    array set params {
466        -color auto
467        -width 1
468        -linestyle solid
469        -brightness 0
470        -raise 0
471        -description ""
472        -param ""
473        -type ""
474    }
475    array set params $settings
476
477    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
478        # can't handle -autocolors yet
479        set params(-color) white
480    }
481    set pos [lsearch -exact $_dlist $dataobj]
482    if {$pos < 0} {
483        lappend _dlist $dataobj
484    }
485    set _obj2ovride($dataobj-color) $params(-color)
486    set _obj2ovride($dataobj-width) $params(-width)
487    set _obj2ovride($dataobj-raise) $params(-raise)
488    $_dispatcher event -idle !rebuild
489}
490
491
492# ----------------------------------------------------------------------
493# USAGE: delete ?<dataobj1> <dataobj2> ...?
494#
495# Clients use this to delete a dataobj from the plot.  If no dataobjs
496# are specified, then all dataobjs are deleted.  No data objects are
497# deleted.  They are only removed from the display list.
498# ----------------------------------------------------------------------
499itcl::body Rappture::VtkImageViewer::delete {args} {
500    if { [llength $args] == 0} {
501        set args $_dlist
502    }
503    # Delete all specified dataobjs
504    set changed 0
505    foreach dataobj $args {
506        set pos [lsearch -exact $_dlist $dataobj]
507        if { $pos < 0 } {
508            continue;                   # Don't know anything about it.
509        }
510        # Remove it from the dataobj list.
511        set _dlist [lreplace $_dlist $pos $pos]
512        array unset _obj2ovride $dataobj-*
513        array unset _settings $dataobj-*
514        set changed 1
515    }
516    # If anything changed, then rebuild the plot
517    if { $changed } {
518        $_dispatcher event -idle !rebuild
519    }
520}
521
522# ----------------------------------------------------------------------
523# USAGE: get ?-objects?
524# USAGE: get ?-visible?
525# USAGE: get ?-image view?
526#
527# Clients use this to query the list of objects being plotted, in
528# order from bottom to top of this result.  The optional "-image"
529# flag can also request the internal images being shown.
530# ----------------------------------------------------------------------
531itcl::body Rappture::VtkImageViewer::get {args} {
532    if {[llength $args] == 0} {
533        set args "-objects"
534    }
535    set op [lindex $args 0]
536    switch -- $op {
537        "-objects" {
538            # put the dataobj list in order according to -raise options
539            set dlist {}
540            foreach dataobj $_dlist {
541                if { ![IsValidObject $dataobj] } {
542                    continue
543                }
544                if {[info exists _obj2ovride($dataobj-raise)] &&
545                    $_obj2ovride($dataobj-raise)} {
546                    set dlist [linsert $dlist 0 $dataobj]
547                } else {
548                    lappend dlist $dataobj
549                }
550            }
551            return $dlist
552        }
553        "-visible" {
554            set dlist {}
555            foreach dataobj $_dlist {
556                if { ![IsValidObject $dataobj] } {
557                    continue
558                }
559                if { ![info exists _obj2ovride($dataobj-raise)] } {
560                    # No setting indicates that the object isn't visible.
561                    continue
562                }
563                # Otherwise use the -raise parameter to put the object to
564                # the front of the list.
565                if { $_obj2ovride($dataobj-raise) } {
566                    set dlist [linsert $dlist 0 $dataobj]
567                } else {
568                    lappend dlist $dataobj
569                }
570            }
571            return $dlist
572        }
573        "-image" {
574            if {[llength $args] != 2} {
575                error "wrong # args: should be \"get -image view\""
576            }
577            switch -- [lindex $args end] {
578                view {
579                    return $_image(plot)
580                }
581                default {
582                    error "bad image name \"[lindex $args end]\": should be view"
583                }
584            }
585        }
586        default {
587            error "bad option \"$op\": should be -objects, -visible or -image"
588        }
589    }
590}
591
592#
593# scale  --
594#
595# This gets called either incrementally as new simulations are
596# added or all at once as a sequence of images.
597# This  accounts for all objects--even those not showing on the
598# screen.  Because of this, the limits are appropriate for all
599# objects as the user scans through data in the ResultSet viewer.
600#
601itcl::body Rappture::VtkImageViewer::scale {args} {
602    foreach dataobj $args {
603        if { ![$dataobj isvalid] } {
604            continue;                   # Object doesn't contain valid data.
605        }
606        foreach axis { x y } {
607            set units [$dataobj hints ${axis}units]
608            set found($units) 1
609            set lim [$dataobj limits $axis]
610            if { ![info exists _limits($axis)] } {
611                set _limits($axis) $lim
612                continue
613            }
614            foreach {min max} $lim break
615            foreach {amin amax} $_limits($axis) break
616            if { $amin > $min } {
617                set amin $min
618            }
619            if { $amax < $max } {
620                set amax $max
621            }
622            set _limits($axis) [list $amin $amax]
623        }
624        foreach { fname lim } [$dataobj fieldlimits] {
625            if { ![info exists _limits($fname)] } {
626                set _limits($fname) $lim
627                continue
628            }
629            foreach {min max} $lim break
630            foreach {fmin fmax} $_limits($fname) break
631            if { $fmin > $min } {
632                set fmin $min
633            }
634            if { $fmax < $max } {
635                set fmax $max
636            }
637            set _limits($fname) [list $fmin $fmax]
638        }
639    }
640    if { [array size found] > 1 } {
641        set _settings(-stretchtofit) 1
642    } else {
643        # Check if the range of the x and y axes requires that we stretch
644        # the contour to fit the plotting area.  This can happen when the
645        # x and y scales differ greatly (> 100x)
646        foreach {xmin xmax} $_limits(x) break
647        foreach {ymin ymax} $_limits(y) break
648        if { (($xmax - $xmin) > (($ymax -$ymin) * $_maxScale)) ||
649             ((($xmax - $xmin) * $_maxScale) < ($ymax -$ymin)) } {
650            set _settings(-stretchtofit) 1
651        }
652    }
653}
654
655# ----------------------------------------------------------------------
656# USAGE: download coming
657# USAGE: download controls <downloadCommand>
658# USAGE: download now
659#
660# Clients use this method to create a downloadable representation
661# of the plot.  Returns a list of the form {ext string}, where
662# "ext" is the file extension (indicating the type of data) and
663# "string" is the data itself.
664# ----------------------------------------------------------------------
665itcl::body Rappture::VtkImageViewer::download {option args} {
666    switch $option {
667        coming {
668            if {[catch {
669                blt::winop snap $itk_component(plotarea) $_image(download)
670            }]} {
671                $_image(download) configure -width 1 -height 1
672                $_image(download) put #000000
673            }
674        }
675        controls {
676            set popup .vtkviewerdownload
677            if { ![winfo exists .vtkviewerdownload] } {
678                set inner [BuildDownloadPopup $popup [lindex $args 0]]
679            } else {
680                set inner [$popup component inner]
681            }
682            set _downloadPopup(image_controls) $inner.image_frame
683            set num [llength [get]]
684            set num [expr {($num == 1) ? "1 result" : "$num results"}]
685            set word [Rappture::filexfer::label downloadWord]
686            $inner.summary configure -text "$word $num in the following format:"
687            update idletasks            ;# Fix initial sizes
688            return $popup
689        }
690        now {
691            set popup .vtkviewerdownload
692            if {[winfo exists .vtkviewerdownload]} {
693                $popup deactivate
694            }
695            switch -- $_downloadPopup(format) {
696                "image" {
697                    return [$this GetImage [lindex $args 0]]
698                }
699                "vtk" {
700                    return [$this GetVtkData [lindex $args 0]]
701                }
702            }
703            return ""
704        }
705        default {
706            error "bad option \"$option\": should be coming, controls, now"
707        }
708    }
709}
710
711# ----------------------------------------------------------------------
712# USAGE: Connect ?<host:port>,<host:port>...?
713#
714# Clients use this method to establish a connection to a new
715# server, or to reestablish a connection to the previous server.
716# Any existing connection is automatically closed.
717# ----------------------------------------------------------------------
718itcl::body Rappture::VtkImageViewer::Connect {} {
719    global readyForNextFrame
720    set readyForNextFrame 1
721    set _hosts [GetServerList "vtkvis"]
722    if { "" == $_hosts } {
723        return 0
724    }
725    set _reset 1
726    set result [VisViewer::Connect $_hosts]
727    if { $result } {
728        if { $_reportClientInfo }  {
729            # Tell the server the viewer, hub, user and session.
730            # Do this immediately on connect before buffering any commands
731            global env
732
733            set info {}
734            set user "???"
735            if { [info exists env(USER)] } {
736                set user $env(USER)
737            }
738            set session "???"
739            if { [info exists env(SESSION)] } {
740                set session $env(SESSION)
741            }
742            lappend info "version" "$Rappture::version"
743            lappend info "build" "$Rappture::build"
744            lappend info "svnurl" "$Rappture::svnurl"
745            lappend info "installdir" "$Rappture::installdir"
746            lappend info "hub" [exec hostname]
747            lappend info "client" "vtkimageviewer"
748            lappend info "user" $user
749            lappend info "session" $session
750            SendCmd "clientinfo [list $info]"
751        }
752        set w [winfo width $itk_component(view)]
753        set h [winfo height $itk_component(view)]
754        EventuallyResize $w $h
755    }
756    return $result
757}
758
759#
760# isconnected --
761#
762# Indicates if we are currently connected to the visualization server.
763#
764itcl::body Rappture::VtkImageViewer::isconnected {} {
765    return [VisViewer::IsConnected]
766}
767
768#
769# disconnect --
770#
771itcl::body Rappture::VtkImageViewer::disconnect {} {
772    Disconnect
773    set _reset 1
774}
775
776#
777# Disconnect --
778#
779# Clients use this method to disconnect from the current rendering server.
780#
781itcl::body Rappture::VtkImageViewer::Disconnect {} {
782    VisViewer::Disconnect
783
784    $_dispatcher cancel !rebuild
785    $_dispatcher cancel !resize
786    $_dispatcher cancel !rotate
787    $_dispatcher cancel !legend
788    # disconnected -- no more data sitting on server
789    array unset _datasets
790    array unset _colormaps
791    global readyForNextFrame
792    set readyForNextFrame 1
793}
794
795# ----------------------------------------------------------------------
796# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
797#
798# Invoked automatically whenever the "image" command comes in from
799# the rendering server.  Indicates that binary image data with the
800# specified <size> will follow.
801# ----------------------------------------------------------------------
802itcl::body Rappture::VtkImageViewer::ReceiveImage { args } {
803    global readyForNextFrame
804    set readyForNextFrame 1
805    array set info {
806        -token "???"
807        -bytes 0
808        -type image
809    }
810    array set info $args
811    set bytes [ReceiveBytes $info(-bytes)]
812    if { $info(-type) == "image" } {
813        if 0 {
814            set f [open "last.ppm" "w"]
815            fconfigure $f -encoding binary
816            puts -nonewline $f $bytes
817            close $f
818        }
819        $_image(plot) configure -data $bytes
820        set time [clock seconds]
821        set date [clock format $time]
822        #puts stderr "$date: received image [image width $_image(plot)]x[image height $_image(plot)] image>"
823        if { $_start > 0 } {
824            set finish [clock clicks -milliseconds]
825            #puts stderr "round trip time [expr $finish -$_start] milliseconds"
826            set _start 0
827        }
828    } elseif { $info(type) == "print" } {
829        set tag $this-print-$info(-token)
830        set _hardcopy($tag) $bytes
831    }
832}
833
834#
835# ReceiveDataset --
836#
837itcl::body Rappture::VtkImageViewer::ReceiveDataset { args } {
838    if { ![isconnected] } {
839        return
840    }
841    set option [lindex $args 0]
842    switch -- $option {
843        "scalar" {
844            set option [lindex $args 1]
845            switch -- $option {
846                "world" {
847                    foreach { x y z value tag } [lrange $args 2 end] break
848                }
849                "pixel" {
850                    foreach { x y value tag } [lrange $args 2 end] break
851                }
852            }
853        }
854        "vector" {
855            set option [lindex $args 1]
856            switch -- $option {
857                "world" {
858                    foreach { x y z vx vy vz tag } [lrange $args 2 end] break
859                }
860                "pixel" {
861                    foreach { x y vx vy vz tag } [lrange $args 2 end] break
862                }
863            }
864        }
865        "names" {
866            foreach { name } [lindex $args 1] {
867                #puts stderr "Dataset: $name"
868            }
869        }
870        default {
871            error "unknown dataset option \"$option\" from server"
872        }
873    }
874}
875
876# ----------------------------------------------------------------------
877# USAGE: Rebuild
878#
879# Called automatically whenever something changes that affects the
880# data in the widget.  Clears any existing data and rebuilds the
881# widget to display new data.
882# ----------------------------------------------------------------------
883itcl::body Rappture::VtkImageViewer::Rebuild {} {
884    set w [winfo width $itk_component(view)]
885    set h [winfo height $itk_component(view)]
886    if { $w < 2 || $h < 2 } {
887        update
888        $_dispatcher event -idle !rebuild
889        return
890    }
891
892    # Turn on buffering of commands to the server.  We don't want to
893    # be preempted by a server disconnect/reconnect (which automatically
894    # generates a new call to Rebuild).
895    StartBufferingCommands
896
897    if { $_width != $w || $_height != $h || $_reset } {
898        set _width $w
899        set _height $h
900        $_arcball resize $w $h
901        DoResize
902        if { $_settings(-stretchtofit) } {
903            AdjustSetting -stretchToFit
904        }
905    }
906    if { $_reset } {
907        #
908        # Reset the camera and other view parameters
909        #
910        InitSettings -view3d -background
911
912        SendCmd "axis lrot z 90"
913        $_arcball quaternion [ViewToQuaternion]
914        if {$_settings(-view3d) } {
915            if { $_view(-ortho)} {
916                SendCmd "camera mode ortho"
917            } else {
918                SendCmd "camera mode persp"
919            }
920            DoRotate
921            SendCmd "camera reset"
922        }
923        PanCamera
924        StopBufferingCommands
925        SendCmd "imgflush"
926        StartBufferingCommands
927    }
928
929    set _first ""
930    # Start off with no datasets visible.
931    SendCmd "dataset visible 0"
932    foreach dataobj [get -objects] {
933        if { [info exists _obj2ovride($dataobj-raise)] &&  $_first == "" } {
934            set _first $dataobj
935            SetCurrentFieldName $dataobj
936        }
937        foreach comp [$dataobj components] {
938            set tag $dataobj-$comp
939            if { ![info exists _datasets($tag)] } {
940                set bytes [$dataobj vtkdata $comp]
941                if 0 {
942                    set f [open /tmp/vtkimage.vtk "w"]
943                    fconfigure $f -translation binary -encoding binary
944                    puts -nonewline $f $bytes
945                    close $f
946                }
947                set length [string length $bytes]
948                if { $_reportClientInfo }  {
949                    set info {}
950                    lappend info "tool_id"       [$dataobj hints toolid]
951                    lappend info "tool_name"     [$dataobj hints toolname]
952                    lappend info "tool_title"    [$dataobj hints tooltitle]
953                    lappend info "tool_command"  [$dataobj hints toolcommand]
954                    lappend info "tool_revision" [$dataobj hints toolrevision]
955                    lappend info "dataset_label" [$dataobj hints label]
956                    lappend info "dataset_size"  $length
957                    lappend info "dataset_tag"   $tag
958                    SendCmd "clientinfo [list $info]"
959                }
960                SendCmd "dataset add $tag data follows $length"
961                SendData $bytes
962                set _datasets($tag) 1
963                SetObjectStyle $dataobj $comp
964            }
965            if { [info exists _obj2ovride($dataobj-raise)] } {
966                # Setting dataset visible enables outline
967                # and image
968                SendCmd "dataset visible 1 $tag"
969            }
970        }
971    }
972
973    InitSettings -stretchtofit -outline
974    if { $_reset } {
975        SendCmd "axis tickpos outside"
976        #SendCmd "axis lformat all %g"
977
978        foreach axis { x y z } {
979            set label ""
980            if { $_first != "" } {
981                set label [$_first hints ${axis}label]
982            }
983            if { $label == "" } {
984                set label [string toupper $axis]
985            }
986            # May be a space in the axis label.
987            SendCmd [list axis name $axis $label]
988
989            set units ""
990            if { $_first != "" } {
991                if {$axis == "z" && [$_first hints ${axis}units] == ""} {
992                    if {$_curFldName != ""} {
993                        set units [lindex $_fields($_curFldName) 1]
994                    }
995                } else {
996                    set units [$_first hints ${axis}units]
997                }
998            }
999            if { $units != "" } {
1000                # May be a space in the axis units.
1001                SendCmd [list axis units $axis $units]
1002            }
1003        }
1004        #
1005        # Reset the camera and other view parameters
1006        #
1007        $_arcball quaternion [ViewToQuaternion]
1008        if {$_settings(-view3d) } {
1009            if { $_view(-ortho)} {
1010                SendCmd "camera mode ortho"
1011            } else {
1012                SendCmd "camera mode persp"
1013            }
1014            DoRotate
1015            SendCmd "camera reset"
1016        }
1017        PanCamera
1018        InitSettings -xgrid -ygrid -zgrid \
1019            -axesvisible -axislabels -field -view3d
1020        if { [array size _fields] < 2 } {
1021            catch {blt::table forget $itk_component(field) $itk_component(field_l)}
1022        }
1023        RequestLegend
1024        set _reset 0
1025    }
1026    global readyForNextFrame
1027    set readyForNextFrame 0;                # Don't advance to the next frame
1028
1029    # Actually write the commands to the server socket.  If it fails, we don't
1030    # care.  We're finished here.
1031    blt::busy hold $itk_component(hull)
1032    StopBufferingCommands
1033    blt::busy release $itk_component(hull)
1034}
1035
1036# ----------------------------------------------------------------------
1037# USAGE: CurrentDatasets ?-all -visible? ?dataobjs?
1038#
1039# Returns a list of server IDs for the current datasets being displayed.  This
1040# is normally a single ID, but it might be a list of IDs if the current data
1041# object has multiple components.
1042# ----------------------------------------------------------------------
1043itcl::body Rappture::VtkImageViewer::CurrentDatasets {args} {
1044    set flag [lindex $args 0]
1045    switch -- $flag {
1046        "-all" {
1047            if { [llength $args] > 1 } {
1048                error "CurrentDatasets: can't specify dataobj after \"-all\""
1049            }
1050            set dlist [get -objects]
1051        }
1052        "-visible" {
1053            if { [llength $args] > 1 } {
1054                set dlist {}
1055                set args [lrange $args 1 end]
1056                foreach dataobj $args {
1057                    if { [info exists _obj2ovride($dataobj-raise)] } {
1058                        lappend dlist $dataobj
1059                    }
1060                }
1061            } else {
1062                set dlist [get -visible]
1063            }
1064        }
1065        default {
1066            set dlist $args
1067        }
1068    }
1069    set rlist ""
1070    foreach dataobj $dlist {
1071        foreach comp [$dataobj components] {
1072            set tag $dataobj-$comp
1073            if { [info exists _datasets($tag)] && $_datasets($tag) } {
1074                lappend rlist $tag
1075            }
1076        }
1077    }
1078    return $rlist
1079}
1080
1081# ----------------------------------------------------------------------
1082# USAGE: Zoom in
1083# USAGE: Zoom out
1084# USAGE: Zoom reset
1085#
1086# Called automatically when the user clicks on one of the zoom
1087# controls for this widget.  Changes the zoom for the current view.
1088# ----------------------------------------------------------------------
1089itcl::body Rappture::VtkImageViewer::Zoom {option} {
1090    switch -- $option {
1091        "in" {
1092            set _view(-zoom) [expr {$_view(-zoom)*1.25}]
1093            SendCmd "camera zoom $_view(-zoom)"
1094        }
1095        "out" {
1096            set _view(-zoom) [expr {$_view(-zoom)*0.8}]
1097            SendCmd "camera zoom $_view(-zoom)"
1098        }
1099        "reset" {
1100            array set _view {
1101                -qw      0.36
1102                -qx      0.25
1103                -qy      0.50
1104                -qz      0.70
1105                -xpan    0
1106                -ypan    0
1107                -zoom    1.0
1108            }
1109            if { $_first != "" } {
1110                set location [$_first hints camera]
1111                if { $location != "" } {
1112                    array set _view $location
1113                }
1114            }
1115            $_arcball quaternion [ViewToQuaternion]
1116            if {$_settings(-view3d) } {
1117                DoRotate
1118            }
1119            SendCmd "camera reset"
1120        }
1121    }
1122}
1123
1124itcl::body Rappture::VtkImageViewer::PanCamera {} {
1125    set x $_view(-xpan)
1126    set y $_view(-ypan)
1127    SendCmd "camera pan $x $y"
1128}
1129
1130
1131# ----------------------------------------------------------------------
1132# USAGE: Rotate click <x> <y>
1133# USAGE: Rotate drag <x> <y>
1134# USAGE: Rotate release <x> <y>
1135#
1136# Called automatically when the user clicks/drags/releases in the
1137# plot area.  Moves the plot according to the user's actions.
1138# ----------------------------------------------------------------------
1139itcl::body Rappture::VtkImageViewer::Rotate {option x y} {
1140    switch -- $option {
1141        "click" {
1142            $itk_component(view) configure -cursor fleur
1143            set _click(x) $x
1144            set _click(y) $y
1145        }
1146        "drag" {
1147            if {[array size _click] == 0} {
1148                Rotate click $x $y
1149            } else {
1150                set w [winfo width $itk_component(view)]
1151                set h [winfo height $itk_component(view)]
1152                if {$w <= 0 || $h <= 0} {
1153                    return
1154                }
1155
1156                if {[catch {
1157                    # this fails sometimes for no apparent reason
1158                    set dx [expr {double($x-$_click(x))/$w}]
1159                    set dy [expr {double($y-$_click(y))/$h}]
1160                }]} {
1161                    return
1162                }
1163                if { $dx == 0 && $dy == 0 } {
1164                    return
1165                }
1166                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1167                EventuallyRotate $q
1168                set _click(x) $x
1169                set _click(y) $y
1170            }
1171        }
1172        "release" {
1173            Rotate drag $x $y
1174            $itk_component(view) configure -cursor ""
1175            catch {unset _click}
1176        }
1177        default {
1178            error "bad option \"$option\": should be click, drag, release"
1179        }
1180    }
1181}
1182
1183itcl::body Rappture::VtkImageViewer::Pick {x y} {
1184    foreach tag [CurrentDatasets -visible] {
1185        SendCmd "dataset getscalar pixel $x $y $tag"
1186    }
1187}
1188
1189# ----------------------------------------------------------------------
1190# USAGE: $this Pan click x y
1191#        $this Pan drag x y
1192#        $this Pan release x y
1193#
1194# Called automatically when the user clicks on one of the zoom
1195# controls for this widget.  Changes the zoom for the current view.
1196# ----------------------------------------------------------------------
1197itcl::body Rappture::VtkImageViewer::Pan {option x y} {
1198    switch -- $option {
1199        "set" {
1200            set w [winfo width $itk_component(view)]
1201            set h [winfo height $itk_component(view)]
1202            set x [expr $x / double($w)]
1203            set y [expr $y / double($h)]
1204            set _view(-xpan) [expr $_view(-xpan) + $x]
1205            set _view(-ypan) [expr $_view(-ypan) + $y]
1206            PanCamera
1207            return
1208        }
1209        "click" {
1210            set _click(x) $x
1211            set _click(y) $y
1212            $itk_component(view) configure -cursor hand1
1213        }
1214        "drag" {
1215            if { ![info exists _click(x)] } {
1216                set _click(x) $x
1217            }
1218            if { ![info exists _click(y)] } {
1219                set _click(y) $y
1220            }
1221            set w [winfo width $itk_component(view)]
1222            set h [winfo height $itk_component(view)]
1223            set dx [expr ($_click(x) - $x)/double($w)]
1224            set dy [expr ($_click(y) - $y)/double($h)]
1225            set _click(x) $x
1226            set _click(y) $y
1227            set _view(-xpan) [expr $_view(-xpan) - $dx]
1228            set _view(-ypan) [expr $_view(-ypan) - $dy]
1229            PanCamera
1230        }
1231        "release" {
1232            Pan drag $x $y
1233            $itk_component(view) configure -cursor ""
1234        }
1235        default {
1236            error "unknown option \"$option\": should set, click, drag, or release"
1237        }
1238    }
1239}
1240
1241# ----------------------------------------------------------------------
1242# USAGE: InitSettings <what> ?<value>?
1243#
1244# Used internally to update rendering settings whenever parameters
1245# change in the popup settings panel.  Sends the new settings off
1246# to the back end.
1247# ----------------------------------------------------------------------
1248itcl::body Rappture::VtkImageViewer::InitSettings { args } {
1249    foreach spec $args {
1250        if { [info exists _settings($_first${spec})] } {
1251            # Reset global setting with dataobj specific setting
1252            set _settings($spec) $_settings($_first${spec})
1253        }
1254        AdjustSetting $spec
1255    }
1256}
1257
1258#
1259# AdjustSetting --
1260#
1261# Changes/updates a specific setting in the widget.  There are
1262# usually user-setable option.  Commands are sent to the render
1263# server.
1264#
1265itcl::body Rappture::VtkImageViewer::AdjustSetting {what {value ""}} {
1266    if { ![isconnected] } {
1267        return
1268    }
1269    switch -- $what {
1270        "-axesvisible" {
1271            set bool $_settings($what)
1272            SendCmd "axis visible all $bool"
1273        }
1274        "-axislabels" {
1275            set bool $_settings($what)
1276            SendCmd "axis labels all $bool"
1277        }
1278        "-axisminorticks" {
1279            set bool $_settings($what)
1280            SendCmd "axis minticks all $bool"
1281        }
1282        "-axismode" {
1283            set mode [$itk_component(axismode) value]
1284            set mode [$itk_component(axismode) translate $mode]
1285            set _settings($what) $mode
1286            SendCmd "axis flymode $mode"
1287        }
1288        "-background" {
1289            set bg [$itk_component(background) value]
1290            array set fgcolors {
1291                "black" "white"
1292                "white" "black"
1293                "grey"  "black"
1294            }
1295            set fg $fgcolors($bg)
1296            configure -plotbackground $bg -plotforeground $fg
1297            $itk_component(view) delete "legend"
1298            SendCmd "screen bgcolor [Color2RGB $bg]"
1299            SendCmd "outline color [Color2RGB $fg]"
1300            SendCmd "axis color all [Color2RGB $fg]"
1301            DrawLegend
1302        }
1303        "-backingcolor" {
1304            set color [$itk_component(backingcolor) value]
1305            if { $color == "none" } {
1306                if { $_settings(-backingvisible) } {
1307                    SendCmd "image backing 0"
1308                    set _settings(-backingvisible) 0
1309                }
1310            } else {
1311                if { !$_settings(-backingvisible) } {
1312                    SendCmd "image backing 1"
1313                    set _settings(-backingvisible) 1
1314                }
1315                SendCmd "image color [Color2RGB $color]"
1316            }
1317        }
1318        "-backingvisible" {
1319            set bool $_settings($what)
1320            SendCmd "image backing $bool"
1321        }
1322        "-colormap" {
1323            set _changed($what) 1
1324            StartBufferingCommands
1325            set color [$itk_component(colormap) value]
1326            set _settings($what) $color
1327            SetCurrentColormap $color
1328            if {$_settings(-colormapdiscrete)} {
1329                 SendCmd "colormap res $_settings(-numcolors) $color"
1330            }
1331            StopBufferingCommands
1332            EventuallyRequestLegend
1333        }
1334        "-colormapdiscrete" {
1335            set bool $_settings($what)
1336            StartBufferingCommands
1337            if {$bool} {
1338                SendCmd "colormap res $_settings(-numcolors)"
1339            } else {
1340                SendCmd "colormap res default"
1341            }
1342            StopBufferingCommands
1343            EventuallyRequestLegend
1344        }
1345        "-field" {
1346            set label [$itk_component(field) value]
1347            set fname [$itk_component(field) translate $label]
1348            set _settings($what) $fname
1349            if { [info exists _fields($fname)] } {
1350                foreach { label units components } $_fields($fname) break
1351                if { $components > 1 } {
1352                    set _colorMode vmag
1353                } else {
1354                    set _colorMode scalar
1355                }
1356                set _curFldName $fname
1357                set _curFldLabel $label
1358            } else {
1359                puts stderr "unknown field \"$fname\""
1360                return
1361            }
1362
1363            # Get the new limits because the field changed.
1364            SendCmd "dataset scalar $_curFldName"
1365            #SendCmd "image colormode scalar $_curFldName"
1366            SendCmd "camera reset"
1367            DrawLegend
1368        }
1369        "-legendvisible" {
1370            if { !$_settings($what) } {
1371                $itk_component(view) delete legend
1372            }
1373            DrawLegend
1374        }
1375        "-level" {
1376            set val $_settings($what)
1377            SendCmd "image level $val"
1378        }
1379        "-opacity" {
1380            set _changed($what) 1
1381            if { $_settings(-view3d) } {
1382                set _settings(-saveopacity) $_settings($what)
1383                set val [expr $_settings($what) * 0.01]
1384                SendCmd "image opacity $val"
1385            } else {
1386                SendCmd "image opacity 1.0"
1387            }
1388        }
1389        "-outline" {
1390            set bool $_settings($what)
1391            SendCmd "outline visible $bool"
1392        }
1393        "-stretchtofit" {
1394            set bool $_settings($what)
1395            if { $bool } {
1396                if { $_settings(-view3d) } {
1397                    SendCmd "camera aspect native"
1398                } else {
1399                    SendCmd "camera aspect window"
1400                }
1401            } else {
1402                SendCmd "camera aspect native"
1403            }
1404        }
1405        "-view3d" {
1406            set bool $_settings($what)
1407            set c $itk_component(view)
1408            StartBufferingCommands
1409            # Fix image scale: 0 for contours, 1 for images.
1410            if { $bool } {
1411                set _settings(-opacity) $_settings(-saveopacity)
1412            } else {
1413                set _settings(-opacity) 100
1414            }
1415            AdjustSetting -opacity
1416            if { $bool } {
1417                $itk_component(opacity) configure -state normal
1418                $itk_component(opacity_l) configure -state normal
1419                if {$_view(-ortho)} {
1420                    SendCmd "camera mode ortho"
1421                } else {
1422                    SendCmd "camera mode persp"
1423                }
1424                SendCmd "camera aspect native"
1425            } else {
1426                $itk_component(opacity) configure -state disabled
1427                $itk_component(opacity_l) configure -state disabled
1428                SendCmd "camera mode image"
1429                if {$_settings(-stretchtofit)} {
1430                    SendCmd "camera aspect window"
1431                }
1432            }
1433            if { $bool } {
1434                set q [ViewToQuaternion]
1435                $_arcball quaternion $q
1436                SendCmd "camera orient $q"
1437            } else {
1438                bind $c <ButtonPress-1> {}
1439                bind $c <B1-Motion> {}
1440                bind $c <ButtonRelease-1> {}
1441            }
1442            SendCmd "camera reset"
1443            # Fix the mouse bindings for rotation/panning and the
1444            # camera mode. Ideally we'd create a bindtag for these.
1445            if { $bool } {
1446                # Bindings for rotation via mouse
1447                bind $c <ButtonPress-1> \
1448                    [itcl::code $this Rotate click %x %y]
1449                bind $c <B1-Motion> \
1450                    [itcl::code $this Rotate drag %x %y]
1451                bind $c <ButtonRelease-1> \
1452                    [itcl::code $this Rotate release %x %y]
1453            }
1454            StopBufferingCommands
1455        }
1456        "-window" {
1457            set val $_settings($what)
1458            SendCmd "image window $val"
1459        }
1460        "-xgrid" - "-ygrid" - "-zgrid" {
1461            set axis [string tolower [string range $what 1 1]]
1462            set bool $_settings($what)
1463            SendCmd "axis grid $axis $bool"
1464        }
1465        default {
1466            error "don't know how to fix $what"
1467        }
1468    }
1469}
1470
1471#
1472# RequestLegend --
1473#
1474# Request a new legend from the server.  The size of the legend
1475# is determined from the height of the canvas.
1476#
1477# This should be called when
1478#   1.  A new current colormap is set.
1479#   2.  Window is resized.
1480#   3.  The limits of the data have changed.  (Just need a redraw).
1481#   4.  Number of isolines have changed. (Just need a redraw).
1482#   5.  Legend becomes visible (Just need a redraw).
1483#
1484itcl::body Rappture::VtkImageViewer::RequestLegend {} {
1485    set _legendPending 0
1486    set font "Arial 8"
1487    set w 12
1488    set lineht [font metrics $font -linespace]
1489    # color ramp height = (canvas height) - (min and max value lines) - 2
1490    set h [expr {$_height - 2 * ($lineht + 2)}]
1491    set _legendHeight $h
1492
1493    set fname $_curFldName
1494    if { [string match "component*" $fname] } {
1495        set title ""
1496    } else {
1497        if { [info exists _fields($fname)] } {
1498            foreach { title units } $_fields($fname) break
1499            if { $units != "" } {
1500                set title [format "%s (%s)" $title $units]
1501            }
1502        } else {
1503            set title $fname
1504        }
1505    }
1506    # If there's a title too, subtract one more line
1507    if { $title != "" } {
1508        incr h -$lineht
1509    }
1510    if { $h < 1 } {
1511        return
1512    }
1513    # Set the legend on the first dataset.
1514    if { $_currentColormap != "" && $_currentColormap != "none" } {
1515        set cmap $_currentColormap
1516        if { ![info exists _colormaps($cmap)] } {
1517            BuildColormap $cmap
1518            set _colormaps($cmap) 1
1519        }
1520        #SendCmd "legend $cmap scalar $_curFldName {} $w $h 0"
1521        SendCmd "legend2 $cmap $w $h"
1522    }
1523}
1524
1525#
1526# SetCurrentColormap --
1527#
1528itcl::body Rappture::VtkImageViewer::SetCurrentColormap { name } {
1529    # Keep track of the colormaps that we build.
1530    if { $name != "none" && ![info exists _colormaps($name)] } {
1531        BuildColormap $name
1532        set _colormaps($name) 1
1533    }
1534    set _currentColormap $name
1535    SendCmd "image colormap $_currentColormap"
1536}
1537
1538
1539#
1540# BuildColormap --
1541#
1542# Build the designated colormap on the server.
1543#
1544itcl::body Rappture::VtkImageViewer::BuildColormap { name } {
1545    set cmap [ColorsToColormap $name]
1546    if { [llength $cmap] == 0 } {
1547        set cmap "0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0"
1548    }
1549    set amap "0.0 1.0 1.0 1.0"
1550    SendCmd "colormap add $name { $cmap } { $amap }"
1551}
1552
1553# ----------------------------------------------------------------------
1554# CONFIGURATION OPTION: -mode
1555# ----------------------------------------------------------------------
1556itcl::configbody Rappture::VtkImageViewer::mode {
1557    switch -- $itk_option(-mode) {
1558        "volume" {
1559            set _settings(-view3d) 1
1560        }
1561        "vtkimage" {
1562            set _settings(-view3d) 0
1563        }
1564        default {
1565            error "unknown mode settings \"$itk_option(-mode)\""
1566        }
1567    }
1568    if { !$_reset } {
1569        AdjustSetting -view3d
1570    }
1571}
1572
1573# ----------------------------------------------------------------------
1574# CONFIGURATION OPTION: -plotbackground
1575# ----------------------------------------------------------------------
1576itcl::configbody Rappture::VtkImageViewer::plotbackground {
1577    if { [isconnected] } {
1578        set rgb [Color2RGB $itk_option(-plotbackground)]
1579        if { !$_reset } {
1580            SendCmd "screen bgcolor $rgb"
1581        }
1582        $itk_component(view) configure -background $itk_option(-plotbackground)
1583    }
1584}
1585
1586# ----------------------------------------------------------------------
1587# CONFIGURATION OPTION: -plotforeground
1588# ----------------------------------------------------------------------
1589itcl::configbody Rappture::VtkImageViewer::plotforeground {
1590    if { [isconnected] } {
1591        set rgb [Color2RGB $itk_option(-plotforeground)]
1592        if { !$_reset } {
1593            SendCmd "axis color all $rgb"
1594            SendCmd "outline color $rgb"
1595        }
1596    }
1597}
1598
1599itcl::body Rappture::VtkImageViewer::BuildImageTab {} {
1600
1601    set fg [option get $itk_component(hull) font Font]
1602    #set bfg [option get $itk_component(hull) boldFont Font]
1603
1604    set inner [$itk_component(main) insert end \
1605        -title "Image Settings" \
1606        -icon [Rappture::icon contour2]]
1607    $inner configure -borderwidth 4
1608
1609    checkbutton $inner.legend \
1610        -text "Legend" \
1611        -variable [itcl::scope _settings(-legendvisible)] \
1612        -command [itcl::code $this AdjustSetting -legendvisible] \
1613        -font "Arial 9"
1614
1615    checkbutton $inner.outline \
1616        -text "Outline" \
1617        -variable [itcl::scope _settings(-outline)] \
1618        -command [itcl::code $this AdjustSetting -outline] \
1619        -font "Arial 9"
1620
1621    checkbutton $inner.backing \
1622        -text "Backing" \
1623        -variable [itcl::scope _settings(-backingvisible)] \
1624        -command [itcl::code $this AdjustSetting -backingvisible] \
1625        -font "Arial 9"
1626
1627    checkbutton $inner.stretch \
1628        -text "Stretch to fit" \
1629        -variable [itcl::scope _settings(-stretchtofit)] \
1630        -command [itcl::code $this AdjustSetting -stretchtofit] \
1631        -font "Arial 9"
1632
1633    checkbutton $inner.colormapDiscrete \
1634        -text "Discrete Colormap" \
1635        -variable [itcl::scope _settings(-colormapdiscrete)] \
1636        -command [itcl::code $this AdjustSetting -colormapdiscrete] \
1637        -font "Arial 9"
1638
1639    itk_component add field_l {
1640        label $inner.field_l -text "Field" -font "Arial 9"
1641    } {
1642        ignore -font
1643    }
1644    itk_component add field {
1645        Rappture::Combobox $inner.field -width 10 -editable 0
1646    }
1647    bind $inner.field <<Value>> \
1648        [itcl::code $this AdjustSetting -field]
1649
1650    label $inner.colormap_l -text "Colormap" -font "Arial 9"
1651    itk_component add colormap {
1652        Rappture::Combobox $inner.colormap -width 10 -editable 0
1653    }
1654    $inner.colormap choices insert end [GetColormapList -includeNone]
1655
1656    $itk_component(colormap) value "BCGYR"
1657    bind $inner.colormap <<Value>> \
1658        [itcl::code $this AdjustSetting -colormap]
1659
1660    label $inner.backingcolor_l -text "Backing Color" -font "Arial 9"
1661    itk_component add backingcolor {
1662        Rappture::Combobox $inner.backingcolor -width 10 -editable 0
1663    }
1664    $inner.backingcolor choices insert end \
1665        "black"              "black"            \
1666        "blue"               "blue"             \
1667        "cyan"               "cyan"             \
1668        "green"              "green"            \
1669        "grey"               "grey"             \
1670        "magenta"            "magenta"          \
1671        "orange"             "orange"           \
1672        "red"                "red"              \
1673        "white"              "white"            \
1674        "none"               "none"
1675
1676    $itk_component(backingcolor) value $_settings(-backingcolor)
1677    bind $inner.backingcolor <<Value>> \
1678        [itcl::code $this AdjustSetting -backingcolor]
1679
1680    label $inner.background_l -text "Background Color" -font "Arial 9"
1681    itk_component add background {
1682        Rappture::Combobox $inner.background -width 10 -editable 0
1683    }
1684    $inner.background choices insert end \
1685        "black"              "black"            \
1686        "white"              "white"            \
1687        "grey"               "grey"
1688
1689    $itk_component(background) value "white"
1690    bind $inner.background <<Value>> \
1691        [itcl::code $this AdjustSetting -background]
1692
1693    itk_component add opacity_l {
1694        label $inner.opacity_l -text "Opacity" -font "Arial 9"
1695    } {
1696        ignore -font
1697    }
1698    itk_component add opacity {
1699        ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1700            -variable [itcl::scope _settings(-opacity)] \
1701            -showvalue off \
1702            -command [itcl::code $this AdjustSetting -opacity]
1703    }
1704
1705    itk_component add window_l {
1706        label $inner.window_l -text "Window" -font "Arial 9"
1707    } {
1708        ignore -font
1709    }
1710    itk_component add window {
1711        ::scale $inner.window -from 0 -to 255 -orient horizontal \
1712            -variable [itcl::scope _settings(-window)] \
1713            -showvalue off \
1714            -command [itcl::code $this AdjustSetting -window]
1715    }
1716    itk_component add level_l {
1717        label $inner.level_l -text "Level" -font "Arial 9"
1718    } {
1719        ignore -font
1720    }
1721    itk_component add level {
1722        ::scale $inner.level -from 0 -to 255 -orient horizontal \
1723            -variable [itcl::scope _settings(-level)] \
1724            -showvalue off \
1725            -command [itcl::code $this AdjustSetting -level]
1726    }
1727
1728    frame $inner.separator1 -height 2 -relief sunken -bd 1
1729    frame $inner.separator2 -height 2 -relief sunken -bd 1
1730
1731    blt::table $inner \
1732        0,0 $inner.field_l -anchor w -pady 2 \
1733        0,1 $inner.field -anchor w -pady 2 -fill x \
1734        1,0 $inner.colormap_l -anchor w -pady 2  \
1735        1,1 $inner.colormap   -anchor w -pady 2 -fill x  \
1736        2,0 $inner.backingcolor_l  -anchor w -pady 2  \
1737        2,1 $inner.backingcolor    -anchor w -pady 2 -fill x  \
1738        3,0 $inner.background_l -anchor w -pady 2 \
1739        3,1 $inner.background -anchor w -pady 2  -fill x \
1740        4,0 $inner.backing -anchor w -pady 2 -cspan 2 \
1741        5,0 $inner.stretch    -anchor w -pady 2 -cspan 2 \
1742        7,0 $inner.legend     -anchor w -pady 2 -cspan 2 \
1743        8,0 $inner.colormapDiscrete -anchor w -pady 2 -cspan 2 \
1744        11,0 $inner.separator1 -padx 2 -fill x -cspan 2 \
1745        12,0 $inner.outline    -anchor w -pady 2 -cspan 2 \
1746        13,0 $inner.separator2 -padx 2 -fill x -cspan 2 \
1747        15,0 $inner.opacity_l -anchor w -pady 2 \
1748        15,1 $inner.opacity   -fill x   -pady 2 \
1749        16,0 $inner.window_l -anchor w -pady 2 \
1750        16,1 $inner.window   -fill x   -pady 2 \
1751        17,0 $inner.level_l -anchor w -pady 2 \
1752        17,1 $inner.level   -fill x   -pady 2
1753
1754    blt::table configure $inner r* c* -resize none
1755    blt::table configure $inner r19 c1 -resize expand
1756}
1757
1758itcl::body Rappture::VtkImageViewer::BuildAxisTab {} {
1759
1760    set fg [option get $itk_component(hull) font Font]
1761    #set bfg [option get $itk_component(hull) boldFont Font]
1762
1763    set inner [$itk_component(main) insert end \
1764        -title "Axis Settings" \
1765        -icon [Rappture::icon axis2]]
1766    $inner configure -borderwidth 4
1767
1768    checkbutton $inner.visible \
1769        -text "Axes" \
1770        -variable [itcl::scope _settings(-axesvisible)] \
1771        -command [itcl::code $this AdjustSetting -axesvisible] \
1772        -font "Arial 9"
1773    checkbutton $inner.labels \
1774        -text "Axis Labels" \
1775        -variable [itcl::scope _settings(-axislabels)] \
1776        -command [itcl::code $this AdjustSetting -axislabels] \
1777        -font "Arial 9"
1778    label $inner.grid_l -text "Grid" -font "Arial 9"
1779    checkbutton $inner.xgrid \
1780        -text "X" \
1781        -variable [itcl::scope _settings(-xgrid)] \
1782        -command [itcl::code $this AdjustSetting -xgrid] \
1783        -font "Arial 9"
1784    checkbutton $inner.ygrid \
1785        -text "Y" \
1786        -variable [itcl::scope _settings(-ygrid)] \
1787        -command [itcl::code $this AdjustSetting -ygrid] \
1788        -font "Arial 9"
1789    checkbutton $inner.zgrid \
1790        -text "Z" \
1791        -variable [itcl::scope _settings(-zgrid)] \
1792        -command [itcl::code $this AdjustSetting -zgrid] \
1793        -font "Arial 9"
1794    checkbutton $inner.minorticks \
1795        -text "Minor Ticks" \
1796        -variable [itcl::scope _settings(-axisminorticks)] \
1797        -command [itcl::code $this AdjustSetting -axisminorticks] \
1798        -font "Arial 9"
1799
1800    label $inner.mode_l -text "Mode" -font "Arial 9"
1801
1802    itk_component add axismode {
1803        Rappture::Combobox $inner.mode -width 10 -editable 0
1804    }
1805    $inner.mode choices insert end \
1806        "static_triad"    "static" \
1807        "closest_triad"   "closest" \
1808        "furthest_triad"  "farthest" \
1809        "outer_edges"     "outer"
1810    $itk_component(axismode) value $_settings(-axismode)
1811    bind $inner.mode <<Value>> [itcl::code $this AdjustSetting -axismode]
1812
1813    blt::table $inner \
1814        0,0 $inner.visible -anchor w -cspan 4 \
1815        1,0 $inner.labels  -anchor w -cspan 4 \
1816        2,0 $inner.minorticks  -anchor w -cspan 4 \
1817        4,0 $inner.grid_l  -anchor w \
1818        4,1 $inner.xgrid   -anchor w \
1819        4,2 $inner.ygrid   -anchor w \
1820        4,3 $inner.zgrid   -anchor w \
1821        5,0 $inner.mode_l  -anchor w -padx { 2 0 } \
1822        5,1 $inner.mode    -fill x -cspan 3
1823
1824    blt::table configure $inner r* c* -resize none
1825    blt::table configure $inner r7 c6 -resize expand
1826    blt::table configure $inner r3 -height 0.125i
1827}
1828
1829itcl::body Rappture::VtkImageViewer::BuildCameraTab {} {
1830    set inner [$itk_component(main) insert end \
1831        -title "Camera Settings" \
1832        -icon [Rappture::icon camera]]
1833    $inner configure -borderwidth 4
1834
1835    label $inner.view_l -text "view" -font "Arial 9"
1836    set f [frame $inner.view]
1837    foreach side { front back left right top bottom } {
1838        button $f.$side  -image [Rappture::icon view$side] \
1839            -command [itcl::code $this SetOrientation $side]
1840        Rappture::Tooltip::for $f.$side "Change the view to $side"
1841        pack $f.$side -side left
1842    }
1843
1844    blt::table $inner \
1845        0,0 $inner.view_l -anchor e -pady 2 \
1846        0,1 $inner.view -anchor w -pady 2
1847    blt::table configure $inner r0 -resize none
1848
1849    set labels { qx qy qz qw xpan ypan zoom }
1850    set row 1
1851    foreach tag $labels {
1852        label $inner.${tag}label -text $tag -font "Arial 9"
1853        entry $inner.${tag} -font "Arial 9"  -bg white \
1854            -textvariable [itcl::scope _view(-$tag)]
1855        bind $inner.${tag} <Return> \
1856            [itcl::code $this camera set -${tag}]
1857        bind $inner.${tag} <KP_Enter> \
1858            [itcl::code $this camera set -${tag}]
1859        blt::table $inner \
1860            $row,0 $inner.${tag}label -anchor e -pady 2 \
1861            $row,1 $inner.${tag} -anchor w -pady 2
1862        blt::table configure $inner r$row -resize none
1863        incr row
1864    }
1865    checkbutton $inner.ortho \
1866        -text "Orthographic Projection" \
1867        -variable [itcl::scope _view(-ortho)] \
1868        -command [itcl::code $this camera set -ortho] \
1869        -font "Arial 9"
1870    blt::table $inner \
1871            $row,0 $inner.ortho -cspan 2 -anchor w -pady 2
1872    blt::table configure $inner r$row -resize none
1873    incr row
1874
1875    blt::table configure $inner c* -resize none
1876    blt::table configure $inner c2 -resize expand
1877    blt::table configure $inner r$row -resize expand
1878}
1879
1880#
1881# camera --
1882#
1883itcl::body Rappture::VtkImageViewer::camera {option args} {
1884    switch -- $option {
1885        "show" {
1886            puts [array get _view]
1887        }
1888        "set" {
1889            set what [lindex $args 0]
1890            set x $_view($what)
1891            set code [catch { string is double $x } result]
1892            if { $code != 0 || !$result } {
1893                return
1894            }
1895            switch -- $what {
1896                "-ortho" {
1897                    if {$_view($what)} {
1898                        SendCmd "camera mode ortho"
1899                    } else {
1900                        SendCmd "camera mode persp"
1901                    }
1902                }
1903                "-xpan" - "-ypan" {
1904                    PanCamera
1905                }
1906                "-qx" - "-qy" - "-qz" - "-qw" {
1907                    set q [ViewToQuaternion]
1908                    $_arcball quaternion $q
1909                    EventuallyRotate $q
1910                }
1911                "-zoom" {
1912                    SendCmd "camera zoom $_view($what)"
1913                }
1914            }
1915        }
1916    }
1917}
1918
1919itcl::body Rappture::VtkImageViewer::GetVtkData { args } {
1920    set bytes ""
1921    foreach dataobj [get] {
1922        foreach comp [$dataobj components] {
1923            set tag $dataobj-$comp
1924            set contents [$dataobj vtkdata $comp]
1925            append bytes "$contents\n"
1926        }
1927    }
1928    return [list .vtk $bytes]
1929}
1930
1931itcl::body Rappture::VtkImageViewer::GetImage { args } {
1932    if { [image width $_image(download)] > 0 &&
1933         [image height $_image(download)] > 0 } {
1934        set bytes [$_image(download) data -format "jpeg -quality 100"]
1935        set bytes [Rappture::encoding::decode -as b64 $bytes]
1936        return [list .jpg $bytes]
1937    }
1938    return ""
1939}
1940
1941itcl::body Rappture::VtkImageViewer::BuildDownloadPopup { popup command } {
1942    Rappture::Balloon $popup \
1943        -title "[Rappture::filexfer::label downloadWord] as..."
1944    set inner [$popup component inner]
1945    label $inner.summary -text "" -anchor w
1946    radiobutton $inner.vtk_button -text "VTK data file" \
1947        -variable [itcl::scope _downloadPopup(format)] \
1948        -font "Arial 9" \
1949        -value vtk
1950    Rappture::Tooltip::for $inner.vtk_button "Save as VTK data file."
1951    radiobutton $inner.image_button -text "Image File" \
1952        -variable [itcl::scope _downloadPopup(format)] \
1953        -font "Arial 9" \
1954        -value image
1955    Rappture::Tooltip::for $inner.image_button \
1956        "Save as digital image."
1957
1958    button $inner.ok -text "Save" \
1959        -highlightthickness 0 -pady 2 -padx 3 \
1960        -command $command \
1961        -compound left \
1962        -image [Rappture::icon download]
1963
1964    button $inner.cancel -text "Cancel" \
1965        -highlightthickness 0 -pady 2 -padx 3 \
1966        -command [list $popup deactivate] \
1967        -compound left \
1968        -image [Rappture::icon cancel]
1969
1970    blt::table $inner \
1971        0,0 $inner.summary -cspan 2  \
1972        1,0 $inner.vtk_button -anchor w -cspan 2 -padx { 4 0 } \
1973        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
1974        4,1 $inner.cancel -width .9i -fill y \
1975        4,0 $inner.ok -padx 2 -width .9i -fill y
1976    blt::table configure $inner r3 -height 4
1977    blt::table configure $inner r4 -pady 4
1978    raise $inner.image_button
1979    $inner.vtk_button invoke
1980    return $inner
1981}
1982
1983#
1984# SetObjectStyle --
1985#
1986# Set the style of the image/contour object.  This gets calls
1987# for each dataset once as it is loaded.  It can overridden by
1988# the user controls.
1989#
1990itcl::body Rappture::VtkImageViewer::SetObjectStyle { dataobj comp } {
1991    # Parse style string.
1992    set tag $dataobj-$comp
1993    array set style {
1994        -color none
1995        -opacity 1.0
1996    }
1997    set stylelist [$dataobj style $comp]
1998    if { $stylelist != "" } {
1999        array set style $stylelist
2000    }
2001    # This is too complicated.  We want to set the colormap, number of
2002    # isolines and opacity for the dataset.  They can be the default values,
2003    # the style hints loaded with the dataset, or set by user controls.  As
2004    # datasets get loaded, they first use the defaults that are overidden
2005    # by the style hints.  If the user changes the global controls, then that
2006    # overrides everything else.  I don't know what it means when global
2007    # controls are specified as style hints by each dataset.  It complicates
2008    # the code to handle aberrant cases.
2009
2010    if { $_changed(-opacity) } {
2011        set style(-opacity) [expr $_settings(-opacity) * 0.01]
2012    }
2013    if { $_changed(-colormap) } {
2014        set style(-color) $_settings(-colormap)
2015    }
2016    if { $_currentColormap == "" } {
2017        $itk_component(colormap) value $style(-color)
2018    }
2019    if { [info exists style(-stretchtofit)] } {
2020        set _settings(-stretchtofit) $style(-stretchtofit)
2021        AdjustSetting -stretchToFit
2022    }
2023    SendCmd "outline add $tag"
2024    SendCmd "outline color [Color2RGB $itk_option(-plotforeground)] $tag"
2025    SendCmd "outline visible $_settings(-outline) $tag"
2026    SendCmd "image add $tag"
2027    SetCurrentColormap $style(-color)
2028    set color [$itk_component(backingcolor) value]
2029    SendCmd "image color [Color2RGB $color] $tag"
2030    SendCmd "image opacity $style(-opacity) $tag"
2031    set _settings(-opacity) [expr $style(-opacity) * 100.0]
2032}
2033
2034itcl::body Rappture::VtkImageViewer::IsValidObject { dataobj } {
2035    if {[catch {$dataobj isa Rappture::Field} valid] != 0 || !$valid} {
2036        return 0
2037    }
2038    return 1
2039}
2040
2041# ----------------------------------------------------------------------
2042# USAGE: ReceiveLegend <colormap> <title> <min> <max> <size>
2043#
2044# Invoked automatically whenever the "legend" command comes in from
2045# the rendering server.  Indicates that binary image data with the
2046# specified <size> will follow.
2047# ----------------------------------------------------------------------
2048itcl::body Rappture::VtkImageViewer::ReceiveLegend { colormap title min max size } {
2049    #puts stderr "ReceiveLegend colormap=$colormap title=$title range=$min,$max size=$size"
2050    if { [isconnected] } {
2051        set bytes [ReceiveBytes $size]
2052        if { ![info exists _image(legend)] } {
2053            set _image(legend) [image create photo]
2054        }
2055        $_image(legend) configure -data $bytes
2056        #puts stderr "read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
2057        if { [catch {DrawLegend} errs] != 0 } {
2058            global errorInfo
2059            puts stderr "errs=$errs errorInfo=$errorInfo"
2060        }
2061    }
2062}
2063
2064#
2065# DrawLegend --
2066#
2067# Draws the legend in its own canvas on the right side of the plot area.
2068#
2069itcl::body Rappture::VtkImageViewer::DrawLegend {} {
2070    set fname $_curFldName
2071    set c $itk_component(view)
2072    set w [winfo width $c]
2073    set h [winfo height $c]
2074    set font "Arial 8"
2075    set lineht [font metrics $font -linespace]
2076
2077    if { [string match "component*" $fname] } {
2078        set title ""
2079    } else {
2080        if { [info exists _fields($fname)] } {
2081            foreach { title units } $_fields($fname) break
2082            if { $units != "" } {
2083                set title [format "%s (%s)" $title $units]
2084            }
2085        } else {
2086            set title $fname
2087        }
2088    }
2089    set x [expr $w - 2]
2090    if { !$_settings(-legendvisible) } {
2091        $c delete legend
2092        return
2093    }
2094    if { [$c find withtag "legend"] == "" } {
2095        set y 2
2096        # If there's a legend title, create a text item for the title.
2097        $c create text $x $y \
2098            -anchor ne \
2099            -fill $itk_option(-plotforeground) -tags "title legend" \
2100            -font $font
2101        if { $title != "" } {
2102            incr y $lineht
2103        }
2104        $c create text $x $y \
2105            -anchor ne \
2106            -fill $itk_option(-plotforeground) -tags "vmax legend" \
2107            -font $font
2108        incr y $lineht
2109        $c create image $x $y \
2110            -anchor ne \
2111            -image $_image(legend) -tags "colormap legend"
2112        $c create rectangle $x $y 1 1 \
2113            -fill "" -outline "" -tags "sensor legend"
2114        $c create text $x [expr {$h-2}] \
2115            -anchor se \
2116            -fill $itk_option(-plotforeground) -tags "vmin legend" \
2117            -font $font
2118        $c bind sensor <Enter> [itcl::code $this EnterLegend %x %y]
2119        $c bind sensor <Leave> [itcl::code $this LeaveLegend]
2120        $c bind sensor <Motion> [itcl::code $this MotionLegend %x %y]
2121    }
2122
2123    set x2 $x
2124    set iw [image width $_image(legend)]
2125    set ih [image height $_image(legend)]
2126    set x1 [expr $x2 - ($iw*12)/10]
2127
2128    $c bind title <ButtonPress> [itcl::code $this LegendTitleAction post]
2129    $c bind title <Enter> [itcl::code $this LegendTitleAction enter]
2130    $c bind title <Leave> [itcl::code $this LegendTitleAction leave]
2131    # Reset the item coordinates according the current size of the plot.
2132    if { [info exists _limits($_curFldName)] } {
2133        foreach { vmin vmax } $_limits($_curFldName) break
2134        $c itemconfigure vmin -text [format %g $vmin]
2135        $c itemconfigure vmax -text [format %g $vmax]
2136    }
2137    set y 2
2138    # If there's a legend title, move the title to the correct position
2139    if { $title != "" } {
2140        $c itemconfigure title -text $title
2141        $c coords title $x $y
2142        incr y $lineht
2143    }
2144    $c coords vmax $x $y
2145    incr y $lineht
2146    $c coords colormap $x $y
2147    $c coords sensor [expr $x - $iw] $y $x [expr $y + $ih]
2148    $c raise sensor
2149    $c coords vmin $x [expr {$h - 2}]
2150}
2151
2152#
2153# EnterLegend --
2154#
2155itcl::body Rappture::VtkImageViewer::EnterLegend { x y } {
2156    SetLegendTip $x $y
2157}
2158
2159#
2160# MotionLegend --
2161#
2162itcl::body Rappture::VtkImageViewer::MotionLegend { x y } {
2163    Rappture::Tooltip::tooltip cancel
2164    set c $itk_component(view)
2165    set cw [winfo width $c]
2166    set ch [winfo height $c]
2167    if { $x >= 0 && $x < $cw && $y >= 0 && $y < $ch } {
2168        SetLegendTip $x $y
2169    }
2170}
2171
2172#
2173# LeaveLegend --
2174#
2175itcl::body Rappture::VtkImageViewer::LeaveLegend { } {
2176    Rappture::Tooltip::tooltip cancel
2177    .rappturetooltip configure -icon ""
2178}
2179
2180#
2181# SetLegendTip --
2182#
2183itcl::body Rappture::VtkImageViewer::SetLegendTip { x y } {
2184    set fname $_curFldName
2185    set c $itk_component(view)
2186    set w [winfo width $c]
2187    set h [winfo height $c]
2188    set font "Arial 8"
2189    set lineht [font metrics $font -linespace]
2190
2191    set ih [image height $_image(legend)]
2192    # Subtract off the offset of the color ramp from the top of the canvas
2193    set iy [expr $y - ($lineht + 2)]
2194
2195    if { [string match "component*" $fname] } {
2196        set title ""
2197    } else {
2198        if { [info exists _fields($fname)] } {
2199            foreach { title units } $_fields($fname) break
2200            if { $units != "" } {
2201                set title [format "%s (%s)" $title $units]
2202            }
2203        } else {
2204            set title $fname
2205        }
2206    }
2207    # If there's a legend title, increase the offset by the line height.
2208    if { $title != "" } {
2209        incr iy -$lineht
2210    }
2211
2212    # Make a swatch of the selected color
2213    if { [catch { $_image(legend) get 10 $iy } pixel] != 0 } {
2214        return
2215    }
2216
2217    if { ![info exists _image(swatch)] } {
2218        set _image(swatch) [image create photo -width 24 -height 24]
2219    }
2220    set color [eval format "\#%02x%02x%02x" $pixel]
2221    $_image(swatch) put black  -to 0 0 23 23
2222    $_image(swatch) put $color -to 1 1 22 22
2223
2224    # Compute the value of the point
2225    if { [info exists _limits($fname)] } {
2226        foreach { vmin vmax } $_limits($fname) break
2227        set t [expr 1.0 - (double($iy) / double($ih-1))]
2228        set value [expr $t * ($vmax - $vmin) + $vmin]
2229    } else {
2230        set value 0.0
2231    }
2232    set tipx [expr $x + 15]
2233    set tipy [expr $y - 5]
2234    .rappturetooltip configure -icon $_image(swatch)
2235    if { [info exists _isolines($y)] } {
2236        Rappture::Tooltip::text $c [format "$title %g (isoline)" $_isolines($y)]
2237    } else {
2238        Rappture::Tooltip::text $c [format "$title %g" $value]
2239    }
2240    Rappture::Tooltip::tooltip show $c +$tipx,+$tipy
2241}
2242
2243# ----------------------------------------------------------------------
2244# USAGE: LegendTitleAction post
2245# USAGE: LegendTitleAction enter
2246# USAGE: LegendTitleAction leave
2247# USAGE: LegendTitleAction save
2248#
2249# Used internally to handle the dropdown list for the fields menu combobox.
2250# The post option is invoked when the field title is pressed to launch the
2251# dropdown.  The enter option is invoked when the user mouses over the field
2252# title. The leave option is invoked when the user moves the mouse away
2253# from the field title.  The save option is invoked whenever there is a
2254# selection from the list, to alert the visualization server.
2255# ----------------------------------------------------------------------
2256itcl::body Rappture::VtkImageViewer::LegendTitleAction {option} {
2257    set c $itk_component(view)
2258    switch -- $option {
2259        post {
2260            foreach { x1 y1 x2 y2 } [$c bbox title] break
2261            set x1 [expr [winfo width $itk_component(view)] - [winfo reqwidth $itk_component(fieldmenu)]]
2262            set x [expr $x1 + [winfo rootx $itk_component(view)]]
2263            set y [expr $y2 + [winfo rooty $itk_component(view)]]
2264            tk_popup $itk_component(fieldmenu) $x $y
2265        }
2266        enter {
2267            $c itemconfigure title -fill red
2268        }
2269        leave {
2270            $c itemconfigure title -fill $itk_option(-plotforeground)
2271        }
2272        save {
2273            $itk_component(field) value $_curFldLabel
2274            AdjustSetting -field
2275        }
2276        default {
2277            error "bad option \"$option\": should be post, enter, leave or save"
2278        }
2279    }
2280}
2281
2282itcl::body Rappture::VtkImageViewer::SetOrientation { side } {
2283    array set positions {
2284        front  "0.707107 0.707107 0 0"
2285        back   "0 0 0.707107 0.707107"
2286        left   "0.5 0.5 -0.5 -0.5"
2287        right  "0.5 0.5 0.5 0.5"
2288        top    "1 0 0 0"
2289        bottom "0 1 0 0"
2290    }
2291    foreach name { -qw -qx -qy -qz } value $positions($side) {
2292        set _view($name) $value
2293    }
2294    set q [ViewToQuaternion]
2295    $_arcball quaternion $q
2296    SendCmd "camera orient $q"
2297    SendCmd "camera reset"
2298    set _view(-xpan) 0
2299    set _view(-ypan) 0
2300    set _view(-zoom) 1.0
2301}
2302
2303itcl::body Rappture::VtkImageViewer::SetCurrentFieldName { dataobj } {
2304    set _first $dataobj
2305    $itk_component(field) choices delete 0 end
2306    $itk_component(fieldmenu) delete 0 end
2307    array unset _fields
2308    set _curFldName ""
2309    foreach cname [$_first components] {
2310        foreach fname [$_first fieldnames $cname] {
2311            if { [info exists _fields($fname)] } {
2312                continue
2313            }
2314            foreach { label units components } \
2315                [$_first fieldinfo $fname] break
2316            $itk_component(field) choices insert end "$fname" "$label"
2317            $itk_component(fieldmenu) add radiobutton -label "$label" \
2318                -value $label -variable [itcl::scope _curFldLabel] \
2319                -selectcolor red \
2320                -activebackground $itk_option(-plotbackground) \
2321                -activeforeground $itk_option(-plotforeground) \
2322                -font "Arial 8" \
2323                -command [itcl::code $this LegendTitleAction save]
2324            set _fields($fname) [list $label $units $components]
2325            if { $_curFldName == "" } {
2326                set _curFldName $fname
2327                set _curFldLabel $label
2328            }
2329        }
2330    }
2331    $itk_component(field) value $_curFldLabel
2332}
Note: See TracBrowser for help on using the repository browser.