source: branches/Rappture 1.2/gui/scripts/vtkvolumeviewer.tcl @ 3311

Last change on this file since 3311 was 3311, checked in by gah, 11 years ago

add emacs mode line to tcl files.

File size: 92.2 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2
3# ----------------------------------------------------------------------
4#  COMPONENT: VtkVolumeViewer - Vtk volume viewer
5#
6#  It connects to the Vtk server running on a rendering farm,
7#  transmits data, and displays the results.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require BLT
17#package require Img
18
19option add *VtkVolumeViewer.width 4i widgetDefault
20option add *VtkVolumeViewer*cursor crosshair widgetDefault
21option add *VtkVolumeViewer.height 4i widgetDefault
22option add *VtkVolumeViewer.foreground black widgetDefault
23option add *VtkVolumeViewer.controlBackground gray widgetDefault
24option add *VtkVolumeViewer.controlDarkBackground #999999 widgetDefault
25option add *VtkVolumeViewer.plotBackground black widgetDefault
26option add *VtkVolumeViewer.plotForeground white widgetDefault
27option add *VtkVolumeViewer.font \
28    -*-helvetica-medium-r-normal-*-12-* widgetDefault
29
30# must use this name -- plugs into Rappture::resources::load
31proc VtkVolumeViewer_init_resources {} {
32    Rappture::resources::register \
33        vtkvis_server Rappture::VtkVolumeViewer::SetServerList
34}
35
36itcl::class Rappture::VtkVolumeViewer {
37    inherit Rappture::VisViewer
38
39    itk_option define -plotforeground plotForeground Foreground ""
40    itk_option define -plotbackground plotBackground Background ""
41
42    constructor { hostlist args } {
43        Rappture::VisViewer::constructor $hostlist
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 limits { colormap }
61    public method sendto { string }
62    public method parameters {title args} {
63        # do nothing
64    }
65    public method scale {args}
66
67    protected method Connect {}
68    protected method CurrentDatasets {args}
69    protected method Disconnect {}
70    protected method DoResize {}
71    protected method DoReseed {}
72    protected method DoRotate {}
73    protected method AdjustSetting {what {value ""}}
74    protected method InitSettings { args  }
75    protected method Pan {option x y}
76    protected method Pick {x y}
77    protected method Rebuild {}
78    protected method ReceiveDataset { args }
79    protected method ReceiveImage { args }
80    protected method ReceiveLegend { colormap title vmin vmax size }
81    protected method Rotate {option x y}
82    protected method SendCmd {string}
83    protected method SendCmdNoWait {string}
84    protected method Zoom {option}
85
86    # The following methods are only used by this class.
87    private method BuildAxisTab {}
88    private method BuildCameraTab {}
89    private method BuildColormap { name colors }
90    private method BuildCutplaneTab {}
91    private method BuildDownloadPopup { widget command }
92    private method BuildVolumeTab {}
93    private method ConvertToVtkData { dataobj comp }
94    private method DrawLegend { title }
95    private method Combo { option }
96    private method EnterLegend { x y }
97    private method EventuallyResize { w h }
98    private method EventuallyReseed { numPoints }
99    private method EventuallyRotate { q }
100    private method EventuallySetCutplane { axis args }
101    private method GetImage { args }
102    private method GetVtkData { args }
103    private method IsValidObject { dataobj }
104    private method LeaveLegend {}
105    private method MotionLegend { x y }
106    private method PanCamera {}
107    private method RequestLegend {}
108    private method SetColormap { dataobj comp }
109    private method ChangeColormap { dataobj comp color }
110    private method ColorsToColormap { color }
111    private method SetLegendTip { x y }
112    private method SetObjectStyle { dataobj comp }
113    private method Slice {option args}
114
115    private variable _arcball ""
116    private variable _outbuf       ;    # buffer for outgoing commands
117
118    private variable _dlist ""     ;    # list of data objects
119    private variable _obj2datasets
120    private variable _obj2ovride   ;    # maps dataobj => style override
121    private variable _datasets     ;    # contains all the dataobj-component
122                                   ;    # datasets in the server
123    private variable _colormaps    ;    # contains all the colormaps
124                                   ;    # in the server.
125    private variable _dataset2style    ;# maps dataobj-component to transfunc
126
127    private variable _click        ;    # info used for rotate operations
128    private variable _limits       ;    # autoscale min/max for all axes
129    private variable _view         ;    # view params for 3D view
130    private variable _settings
131    private variable _style;            # Array of current component styles.
132    private variable _initialStyle;     # Array of initial component styles.
133    private variable _reset 1;          # indicates if camera needs to be reset
134                                        # to starting position.
135
136    private variable _first ""     ;    # This is the topmost dataset.
137    private variable _start 0
138    private variable _buffering 0
139    private variable _title ""
140    private variable _seeds
141
142    common _downloadPopup;              # download options from popup
143    private common _hardcopy
144    private variable _width 0
145    private variable _height 0
146    private variable _resizePending 0
147    private variable _reseedPending 0
148    private variable _rotatePending 0
149    private variable _cutplanePending 0
150    private variable _legendPending 0
151    private variable _outline
152    private variable _vectorFields
153    private variable _scalarFields
154    private variable _fields
155    private variable _currentField ""
156    private variable _field      ""
157    private variable _numSeeds 200
158    private variable _colorMode "vmag";#  Mode of colormap (vmag or scalar)
159}
160
161itk::usual VtkVolumeViewer {
162    keep -background -foreground -cursor -font
163    keep -plotbackground -plotforeground
164}
165
166# ----------------------------------------------------------------------
167# CONSTRUCTOR
168# ----------------------------------------------------------------------
169itcl::body Rappture::VtkVolumeViewer::constructor {hostlist args} {
170    package require vtk
171    set _serverType "vtkvis"
172
173    # Rebuild event
174    $_dispatcher register !rebuild
175    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
176
177    # Resize event
178    $_dispatcher register !resize
179    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
180
181    # Reseed event
182    $_dispatcher register !reseed
183    $_dispatcher dispatch $this !reseed "[itcl::code $this DoReseed]; list"
184
185    # Rotate event
186    $_dispatcher register !rotate
187    $_dispatcher dispatch $this !rotate "[itcl::code $this DoRotate]; list"
188
189    # Legend event
190    $_dispatcher register !legend
191    $_dispatcher dispatch $this !legend "[itcl::code $this RequestLegend]; list"
192
193    # X-Cutplane event
194    $_dispatcher register !xcutplane
195    $_dispatcher dispatch $this !xcutplane \
196        "[itcl::code $this AdjustSetting cutplane-xposition]; list"
197
198    # Y-Cutplane event
199    $_dispatcher register !ycutplane
200    $_dispatcher dispatch $this !ycutplane \
201        "[itcl::code $this AdjustSetting cutplane-yposition]; list"
202
203    # Z-Cutplane event
204    $_dispatcher register !zcutplane
205    $_dispatcher dispatch $this !zcutplane \
206        "[itcl::code $this AdjustSetting cutplane-zposition]; list"
207
208    #
209    # Populate parser with commands handle incoming requests
210    #
211    $_parser alias image [itcl::code $this ReceiveImage]
212    $_parser alias dataset [itcl::code $this ReceiveDataset]
213    $_parser alias legend [itcl::code $this ReceiveLegend]
214
215    array set _outline {
216        id -1
217        afterId -1
218        x1 -1
219        y1 -1
220        x2 -1
221        y2 -1
222    }
223    # Initialize the view to some default parameters.
224    array set _view {
225        qw              0.853553
226        qx              -0.353553
227        qy              0.353553
228        qz              0.146447
229        zoom            1.0
230        xpan            0
231        ypan            0
232        ortho           0
233    }
234    set _arcball [blt::arcball create 100 100]
235    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
236    $_arcball quaternion $q
237
238    set _limits(zmin) 0.0
239    set _limits(zmax) 1.0
240
241    array set _settings [subst {
242        axis-xgrid              0
243        axis-ygrid              0
244        axis-zgrid              0
245        axis-xcutplane          0
246        axis-ycutplane          0
247        axis-zcutplane          0
248        axis-xposition          0
249        axis-yposition          0
250        axis-zposition          0
251        axis-visible            1
252        axis-labels             1
253        cutplane-edges          0
254        cutplane-xvisible       0
255        cutplane-yvisible       0
256        cutplane-zvisible       0
257        cutplane-xposition      50
258        cutplane-yposition      50
259        cutplane-zposition      50
260        cutplane-visible        1
261        cutplane-lighting       1
262        cutplane-wireframe      0
263        cutplane-opacity        100
264        volume-lighting         1
265        volume-material         80
266        volume-opacity          40
267        volume-quality          50
268        volume-visible          1
269        legend-visible          1
270    }]
271
272    itk_component add view {
273        canvas $itk_component(plotarea).view \
274            -highlightthickness 0 -borderwidth 0
275    } {
276        usual
277        ignore -highlightthickness -borderwidth  -background
278    }
279
280    itk_component add fieldmenu {
281        menu $itk_component(plotarea).menu -bg black -fg white -relief flat \
282            -tearoff no
283    } {
284        usual
285        ignore -background -foreground -relief -tearoff
286    }
287    set c $itk_component(view)
288    bind $c <Configure> [itcl::code $this EventuallyResize %w %h]
289    bind $c <4> [itcl::code $this Zoom in 0.25]
290    bind $c <5> [itcl::code $this Zoom out 0.25]
291    bind $c <KeyPress-Left>  [list %W xview scroll 10 units]
292    bind $c <KeyPress-Right> [list %W xview scroll -10 units]
293    bind $c <KeyPress-Up>    [list %W yview scroll 10 units]
294    bind $c <KeyPress-Down>  [list %W yview scroll -10 units]
295    bind $c <Enter> "focus %W"
296    bind $c <Control-F1> [itcl::code $this ToggleConsole]
297
298    # Fixes the scrollregion in case we go off screen
299    $c configure -scrollregion [$c bbox all]
300
301    set _map(id) [$c create image 0 0 -anchor nw -image $_image(plot)]
302    set _map(cwidth) -1
303    set _map(cheight) -1
304    set _map(zoom) 1.0
305    set _map(original) ""
306
307    set f [$itk_component(main) component controls]
308    itk_component add reset {
309        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
310            -highlightthickness 0 \
311            -image [Rappture::icon reset-view] \
312            -command [itcl::code $this Zoom reset]
313    } {
314        usual
315        ignore -highlightthickness
316    }
317    pack $itk_component(reset) -side top -padx 2 -pady 2
318    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
319
320    itk_component add zoomin {
321        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
322            -highlightthickness 0 \
323            -image [Rappture::icon zoom-in] \
324            -command [itcl::code $this Zoom in]
325    } {
326        usual
327        ignore -highlightthickness
328    }
329    pack $itk_component(zoomin) -side top -padx 2 -pady 2
330    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
331
332    itk_component add zoomout {
333        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
334            -highlightthickness 0 \
335            -image [Rappture::icon zoom-out] \
336            -command [itcl::code $this Zoom out]
337    } {
338        usual
339        ignore -highlightthickness
340    }
341    pack $itk_component(zoomout) -side top -padx 2 -pady 2
342    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
343
344    itk_component add volume {
345        Rappture::PushButton $f.volume \
346            -onimage [Rappture::icon volume-on] \
347            -offimage [Rappture::icon volume-off] \
348            -variable [itcl::scope _settings(volume-visible)] \
349            -command [itcl::code $this AdjustSetting volume-visible]
350    }
351    $itk_component(volume) select
352    Rappture::Tooltip::for $itk_component(volume) \
353        "Don't display the volume"
354    pack $itk_component(volume) -padx 2 -pady 2
355
356    itk_component add cutplane {
357        Rappture::PushButton $f.cutplane \
358            -onimage [Rappture::icon cutbutton] \
359            -offimage [Rappture::icon cutbutton] \
360            -variable [itcl::scope _settings(cutplane-visible)] \
361            -command [itcl::code $this AdjustSetting cutplane-visible]
362    }
363    $itk_component(cutplane) select
364    Rappture::Tooltip::for $itk_component(cutplane) \
365        "Show/Hide cutplanes"
366    pack $itk_component(cutplane) -padx 2 -pady 2
367
368
369    if { [catch {
370        BuildVolumeTab
371        BuildCutplaneTab
372        BuildAxisTab
373        BuildCameraTab
374    } errs] != 0 } {
375        puts stderr errs=$errs
376    }
377    # Legend
378
379    set _image(legend) [image create photo]
380    itk_component add legend {
381        canvas $itk_component(plotarea).legend -width 50 -highlightthickness 0
382    } {
383        usual
384        ignore -highlightthickness
385        rename -background -plotbackground plotBackground Background
386    }
387
388    # Hack around the Tk panewindow.  The problem is that the requested
389    # size of the 3d view isn't set until an image is retrieved from
390    # the server.  So the panewindow uses the tiny size.
391    set w 10000
392    pack forget $itk_component(view)
393    blt::table $itk_component(plotarea) \
394        0,0 $itk_component(view) -fill both -reqwidth $w
395    blt::table configure $itk_component(plotarea) c1 -resize none
396
397    # Bindings for rotation via mouse
398    bind $itk_component(view) <ButtonPress-1> \
399        [itcl::code $this Rotate click %x %y]
400    bind $itk_component(view) <B1-Motion> \
401        [itcl::code $this Rotate drag %x %y]
402    bind $itk_component(view) <ButtonRelease-1> \
403        [itcl::code $this Rotate release %x %y]
404    bind $itk_component(view) <Configure> \
405        [itcl::code $this EventuallyResize %w %h]
406
407    if 0 {
408    bind $itk_component(view) <Configure> \
409        [itcl::code $this EventuallyResize %w %h]
410    }
411    # Bindings for panning via mouse
412    bind $itk_component(view) <ButtonPress-2> \
413        [itcl::code $this Pan click %x %y]
414    bind $itk_component(view) <B2-Motion> \
415        [itcl::code $this Pan drag %x %y]
416    bind $itk_component(view) <ButtonRelease-2> \
417        [itcl::code $this Pan release %x %y]
418
419    bind $itk_component(view) <ButtonRelease-3> \
420        [itcl::code $this Pick %x %y]
421
422    # Bindings for panning via keyboard
423    bind $itk_component(view) <KeyPress-Left> \
424        [itcl::code $this Pan set -10 0]
425    bind $itk_component(view) <KeyPress-Right> \
426        [itcl::code $this Pan set 10 0]
427    bind $itk_component(view) <KeyPress-Up> \
428        [itcl::code $this Pan set 0 -10]
429    bind $itk_component(view) <KeyPress-Down> \
430        [itcl::code $this Pan set 0 10]
431    bind $itk_component(view) <Shift-KeyPress-Left> \
432        [itcl::code $this Pan set -2 0]
433    bind $itk_component(view) <Shift-KeyPress-Right> \
434        [itcl::code $this Pan set 2 0]
435    bind $itk_component(view) <Shift-KeyPress-Up> \
436        [itcl::code $this Pan set 0 -2]
437    bind $itk_component(view) <Shift-KeyPress-Down> \
438        [itcl::code $this Pan set 0 2]
439
440    # Bindings for zoom via keyboard
441    bind $itk_component(view) <KeyPress-Prior> \
442        [itcl::code $this Zoom out]
443    bind $itk_component(view) <KeyPress-Next> \
444        [itcl::code $this Zoom in]
445
446    bind $itk_component(view) <Enter> "focus $itk_component(view)"
447
448    if {[string equal "x11" [tk windowingsystem]]} {
449        # Bindings for zoom via mouse
450        bind $itk_component(view) <4> [itcl::code $this Zoom out]
451        bind $itk_component(view) <5> [itcl::code $this Zoom in]
452    }
453
454    set _image(download) [image create photo]
455
456    eval itk_initialize $args
457    Connect
458    update
459}
460
461# ----------------------------------------------------------------------
462# DESTRUCTOR
463# ----------------------------------------------------------------------
464itcl::body Rappture::VtkVolumeViewer::destructor {} {
465    Disconnect
466    image delete $_image(plot)
467    image delete $_image(download)
468    catch { blt::arcball destroy $_arcball }
469}
470
471itcl::body Rappture::VtkVolumeViewer::DoResize {} {
472    if { $_width < 2 } {
473        set _width 500
474    }
475    if { $_height < 2 } {
476        set _height 500
477    }
478    set _start [clock clicks -milliseconds]
479    SendCmd "screen size $_width $_height"
480    set _legendPending 1
481
482    #SendCmd "imgflush"
483
484    # Must reset camera to have object scaling to take effect.
485    #SendCmd "camera reset"
486    #SendCmd "camera zoom $_view(zoom)"
487    set _resizePending 0
488}
489
490itcl::body Rappture::VtkVolumeViewer::DoRotate {} {
491    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
492    SendCmd "camera orient $q"
493    set _rotatePending 0
494}
495
496itcl::body Rappture::VtkVolumeViewer::EventuallyResize { w h } {
497    set _width $w
498    set _height $h
499    $_arcball resize $w $h
500    if { !$_resizePending } {
501        set _resizePending 1
502        $_dispatcher event -after 400 !resize
503    }
504}
505
506itcl::body Rappture::VtkVolumeViewer::EventuallyReseed { numPoints } {
507    set _numSeeds $numPoints
508    if { !$_reseedPending } {
509        set _reseedPending 1
510        $_dispatcher event -after 600 !reseed
511    }
512}
513
514set rotate_delay 100
515
516itcl::body Rappture::VtkVolumeViewer::EventuallyRotate { q } {
517    foreach { _view(qw) _view(qx) _view(qy) _view(qz) } $q break
518    if { !$_rotatePending } {
519        set _rotatePending 1
520        global rotate_delay
521        $_dispatcher event -after $rotate_delay !rotate
522    }
523}
524
525itcl::body Rappture::VtkVolumeViewer::EventuallySetCutplane { axis args } {
526    if { !$_cutplanePending } {
527        set _cutplanePending 1
528        $_dispatcher event -after 100 !${axis}cutplane
529    }
530}
531
532# ----------------------------------------------------------------------
533# USAGE: add <dataobj> ?<settings>?
534#
535# Clients use this to add a data object to the plot.  The optional
536# <settings> are used to configure the plot.  Allowed settings are
537# -color, -brightness, -width, -linestyle, and -raise.
538# ----------------------------------------------------------------------
539itcl::body Rappture::VtkVolumeViewer::add {dataobj {settings ""}} {
540    array set params {
541        -color auto
542        -width 1
543        -linestyle solid
544        -brightness 0
545        -raise 0
546        -description ""
547        -param ""
548        -type ""
549    }
550    array set params $settings
551    set params(-description) ""
552    set params(-param) ""
553    foreach {opt val} $settings {
554        if {![info exists params($opt)]} {
555            error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]"
556        }
557        set params($opt) $val
558    }
559    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
560        # can't handle -autocolors yet
561        set params(-color) black
562    }
563    set pos [lsearch -exact $dataobj $_dlist]
564    if {$pos < 0} {
565        lappend _dlist $dataobj
566    }
567    set _obj2ovride($dataobj-color) $params(-color)
568    set _obj2ovride($dataobj-width) $params(-width)
569    set _obj2ovride($dataobj-raise) $params(-raise)
570    $_dispatcher event -idle !rebuild
571}
572
573
574# ----------------------------------------------------------------------
575# USAGE: delete ?<dataobj1> <dataobj2> ...?
576#
577#       Clients use this to delete a dataobj from the plot.  If no dataobjs
578#       are specified, then all dataobjs are deleted.  No data objects are
579#       deleted.  They are only removed from the display list.
580#
581# ----------------------------------------------------------------------
582itcl::body Rappture::VtkVolumeViewer::delete {args} {
583    if { [llength $args] == 0} {
584        set args $_dlist
585    }
586    # Delete all specified dataobjs
587    set changed 0
588    foreach dataobj $args {
589        set pos [lsearch -exact $_dlist $dataobj]
590        if { $pos < 0 } {
591            continue;                   # Don't know anything about it.
592        }
593        # Remove it from the dataobj list.
594        set _dlist [lreplace $_dlist $pos $pos]
595        SendCmd "dataset visible 0"
596        array unset _obj2ovride $dataobj-*
597        array unset _settings $dataobj-*
598        # Append to the end of the dataobj list.
599        lappend _dlist $dataobj
600        set changed 1
601    }
602    # If anything changed, then rebuild the plot
603    if { $changed } {
604        $_dispatcher event -idle !rebuild
605    }
606}
607
608# ----------------------------------------------------------------------
609# USAGE: get ?-objects?
610# USAGE: get ?-visible?
611# USAGE: get ?-image view?
612#
613# Clients use this to query the list of objects being plotted, in
614# order from bottom to top of this result.  The optional "-image"
615# flag can also request the internal images being shown.
616# ----------------------------------------------------------------------
617itcl::body Rappture::VtkVolumeViewer::get {args} {
618    if {[llength $args] == 0} {
619        set args "-objects"
620    }
621
622    set op [lindex $args 0]
623    switch -- $op {
624        "-objects" {
625            # put the dataobj list in order according to -raise options
626            set dlist {}
627            foreach dataobj $_dlist {
628                if { ![IsValidObject $dataobj] } {
629                    continue
630                }
631                if {[info exists _obj2ovride($dataobj-raise)] &&
632                    $_obj2ovride($dataobj-raise)} {
633                    set dlist [linsert $dlist 0 $dataobj]
634                } else {
635                    lappend dlist $dataobj
636                }
637            }
638            return $dlist
639        }
640        "-visible" {
641            set dlist {}
642            foreach dataobj $_dlist {
643                if { ![IsValidObject $dataobj] } {
644                    continue
645                }
646                if { ![info exists _obj2ovride($dataobj-raise)] } {
647                    # No setting indicates that the object isn't visible.
648                    continue
649                }
650                # Otherwise use the -raise parameter to put the object to
651                # the front of the list.
652                if { $_obj2ovride($dataobj-raise) } {
653                    set dlist [linsert $dlist 0 $dataobj]
654                } else {
655                    lappend dlist $dataobj
656                }
657            }
658            return $dlist
659        }           
660        -image {
661            if {[llength $args] != 2} {
662                error "wrong # args: should be \"get -image view\""
663            }
664            switch -- [lindex $args end] {
665                view {
666                    return $_image(plot)
667                }
668                default {
669                    error "bad image name \"[lindex $args end]\": should be view"
670                }
671            }
672        }
673        default {
674            error "bad option \"$op\": should be -objects or -image"
675        }
676    }
677}
678
679# ----------------------------------------------------------------------
680# USAGE: scale ?<data1> <data2> ...?
681#
682# Sets the default limits for the overall plot according to the
683# limits of the data for all of the given <data> objects.  This
684# accounts for all objects--even those not showing on the screen.
685# Because of this, the limits are appropriate for all objects as
686# the user scans through data in the ResultSet viewer.
687# ----------------------------------------------------------------------
688itcl::body Rappture::VtkVolumeViewer::scale {args} {
689    array unset _limits
690    foreach dataobj $args {
691        set string [limits $dataobj]
692        if { $string == "" } {
693            continue
694        }
695        array set bounds $string
696        if {![info exists _limits(xmin)] || $_limits(xmin) > $bounds(xmin)} {
697            set _limits(xmin) $bounds(xmin)
698        }
699        if {![info exists _limits(xmax)] || $_limits(xmax) < $bounds(xmax)} {
700            set _limits(xmax) $bounds(xmax)
701        }
702
703        if {![info exists _limits(ymin)] || $_limits(ymin) > $bounds(ymin)} {
704            set _limits(ymin) $bounds(ymin)
705        }
706        if {![info exists _limits(ymax)] || $_limits(ymax) < $bounds(ymax)} {
707            set _limits(ymax) $bounds(ymax)
708        }
709
710        if {![info exists _limits(zmin)] || $_limits(zmin) > $bounds(zmin)} {
711            set _limits(zmin) $bounds(zmin)
712        }
713        if {![info exists _limits(zmax)] || $_limits(zmax) < $bounds(zmax)} {
714            set _limits(zmax) $bounds(zmax)
715        }
716    }
717}
718
719# ----------------------------------------------------------------------
720# USAGE: download coming
721# USAGE: download controls <downloadCommand>
722# USAGE: download now
723#
724# Clients use this method to create a downloadable representation
725# of the plot.  Returns a list of the form {ext string}, where
726# "ext" is the file extension (indicating the type of data) and
727# "string" is the data itself.
728# ----------------------------------------------------------------------
729itcl::body Rappture::VtkVolumeViewer::download {option args} {
730    switch $option {
731        coming {
732            if {[catch {
733                blt::winop snap $itk_component(plotarea) $_image(download)
734            }]} {
735                $_image(download) configure -width 1 -height 1
736                $_image(download) put #000000
737            }
738        }
739        controls {
740            set popup .vtkviewerdownload
741            if { ![winfo exists .vtkviewerdownload] } {
742                set inner [BuildDownloadPopup $popup [lindex $args 0]]
743            } else {
744                set inner [$popup component inner]
745            }
746            set _downloadPopup(image_controls) $inner.image_frame
747            set num [llength [get]]
748            set num [expr {($num == 1) ? "1 result" : "$num results"}]
749            set word [Rappture::filexfer::label downloadWord]
750            $inner.summary configure -text "$word $num in the following format:"
751            update idletasks            ;# Fix initial sizes
752            return $popup
753        }
754        now {
755            set popup .vtkviewerdownload
756            if {[winfo exists .vtkviewerdownload]} {
757                $popup deactivate
758            }
759            switch -- $_downloadPopup(format) {
760                "image" {
761                    return [$this GetImage [lindex $args 0]]
762                }
763                "vtk" {
764                    return [$this GetVtkData [lindex $args 0]]
765                }
766            }
767            return ""
768        }
769        default {
770            error "bad option \"$option\": should be coming, controls, now"
771        }
772    }
773}
774
775# ----------------------------------------------------------------------
776# USAGE: Connect ?<host:port>,<host:port>...?
777#
778# Clients use this method to establish a connection to a new
779# server, or to reestablish a connection to the previous server.
780# Any existing connection is automatically closed.
781# ----------------------------------------------------------------------
782itcl::body Rappture::VtkVolumeViewer::Connect {} {
783    set _hosts [GetServerList "vtkvis"]
784    if { "" == $_hosts } {
785        return 0
786    }
787    set result [VisViewer::Connect $_hosts]
788    if { $result } {
789        set w [winfo width $itk_component(view)]
790        set h [winfo height $itk_component(view)]
791        EventuallyResize $w $h
792    }
793    return $result
794}
795
796#
797# isconnected --
798#
799#       Indicates if we are currently connected to the visualization server.
800#
801itcl::body Rappture::VtkVolumeViewer::isconnected {} {
802    return [VisViewer::IsConnected]
803}
804
805#
806# disconnect --
807#
808itcl::body Rappture::VtkVolumeViewer::disconnect {} {
809    Disconnect
810    set _reset 1
811}
812
813#
814# Disconnect --
815#
816#       Clients use this method to disconnect from the current rendering
817#       server.
818#
819itcl::body Rappture::VtkVolumeViewer::Disconnect {} {
820    VisViewer::Disconnect
821
822    $_dispatcher cancel !rebuild
823    $_dispatcher cancel !resize
824    $_dispatcher cancel !reseed
825    $_dispatcher cancel !rotate
826    $_dispatcher cancel !xcutplane
827    $_dispatcher cancel !ycutplane
828    $_dispatcher cancel !zcutplane
829    $_dispatcher cancel !legend
830    # disconnected -- no more data sitting on server
831    set _outbuf ""
832    array unset _datasets
833    array unset _data
834    array unset _colormaps
835    array unset _seeds
836    array unset _dataset2style
837    array unset _obj2datasets
838}
839
840#
841# sendto --
842#
843itcl::body Rappture::VtkVolumeViewer::sendto { bytes } {
844    SendBytes "$bytes\n"
845    StartWaiting
846}
847
848#
849# SendCmd
850#
851#       Send commands off to the rendering server.  If we're currently
852#       sending data objects to the server, buffer the commands to be
853#       sent later.
854#
855itcl::body Rappture::VtkVolumeViewer::SendCmd {string} {
856    if { $_buffering } {
857        append _outbuf $string "\n"
858    } else {
859        SendBytes "$string\n"
860        StartWaiting
861    }
862}
863
864#
865# SendCmdNoWait
866#
867#       Send commands off to the rendering server.  If we're currently
868#       sending data objects to the server, buffer the commands to be
869#       sent later.
870#
871itcl::body Rappture::VtkVolumeViewer::SendCmdNoWait {string} {
872    if { $_buffering } {
873        append _outbuf $string "\n"
874    } else {
875        SendBytes "$string\n"
876    }
877}
878
879# ----------------------------------------------------------------------
880# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
881#
882# Invoked automatically whenever the "image" command comes in from
883# the rendering server.  Indicates that binary image data with the
884# specified <size> will follow.
885# ----------------------------------------------------------------------
886itcl::body Rappture::VtkVolumeViewer::ReceiveImage { args } {
887    array set info {
888        -token "???"
889        -bytes 0
890        -type image
891    }
892    array set info $args
893    set bytes [ReceiveBytes $info(-bytes)]
894    StopWaiting
895    if { $info(-type) == "image" } {
896        if 0 {
897            set f [open "last.ppm" "w"]
898            puts $f $bytes
899            close $f
900        }
901        $_image(plot) configure -data $bytes
902        set time [clock seconds]
903        set date [clock format $time]
904        #puts stderr "$date: received image [image width $_image(plot)]x[image height $_image(plot)] image>"       
905        if { $_start > 0 } {
906            set finish [clock clicks -milliseconds]
907            #puts stderr "round trip time [expr $finish -$_start] milliseconds"
908            set _start 0
909        }
910    } elseif { $info(type) == "print" } {
911        set tag $this-print-$info(-token)
912        set _hardcopy($tag) $bytes
913    }
914    if { $_legendPending } {
915        RequestLegend
916    }
917}
918
919#
920# ReceiveDataset --
921#
922itcl::body Rappture::VtkVolumeViewer::ReceiveDataset { args } {
923    if { ![isconnected] } {
924        return
925    }
926    set option [lindex $args 0]
927    switch -- $option {
928        "scalar" {
929            set option [lindex $args 1]
930            switch -- $option {
931                "world" {
932                    foreach { x y z value tag } [lrange $args 2 end] break
933                }
934                "pixel" {
935                    foreach { x y value tag } [lrange $args 2 end] break
936                }
937            }
938        }
939        "vector" {
940            set option [lindex $args 1]
941            switch -- $option {
942                "world" {
943                    foreach { x y z vx vy vz tag } [lrange $args 2 end] break
944                }
945                "pixel" {
946                    foreach { x y vx vy vz tag } [lrange $args 2 end] break
947                }
948            }
949        }
950        "names" {
951            foreach { name } [lindex $args 1] {
952                #puts stderr "Dataset: $name"
953            }
954        }
955        default {
956            error "unknown dataset option \"$option\" from server"
957        }
958    }
959}
960
961# ----------------------------------------------------------------------
962# USAGE: Rebuild
963#
964# Called automatically whenever something changes that affects the
965# data in the widget.  Clears any existing data and rebuilds the
966# widget to display new data.
967# ----------------------------------------------------------------------
968itcl::body Rappture::VtkVolumeViewer::Rebuild {} {
969    update
970    set w [winfo width $itk_component(view)]
971    set h [winfo height $itk_component(view)]
972    if { $w < 2 || $h < 2 } {
973        $_dispatcher event -idle !rebuild
974        return
975    }
976
977    set _buffering 1
978    set _legendPending 1
979    # Turn on buffering of commands to the server.  We don't want to
980    # be preempted by a server disconnect/reconnect (which automatically
981    # generates a new call to Rebuild).   
982    set _width $w
983    set _height $h
984    $_arcball resize $w $h
985    DoResize
986    #
987    # Reset the camera and other view parameters
988    #
989    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
990    $_arcball quaternion $q
991    if {$_view(ortho)} {
992        SendCmd "camera mode ortho"
993    } else {
994        SendCmd "camera mode persp"
995    }
996    DoRotate
997    PanCamera
998    set _first ""
999    InitSettings axis-xgrid axis-ygrid axis-zgrid axis-mode \
1000        axis-visible axis-labels
1001
1002    SendCmd "imgflush"
1003
1004    set _limits(zmin) ""
1005    set _limits(zmax) ""
1006    set _first ""
1007
1008    foreach dataobj [get -objects] {
1009        if { [info exists _obj2ovride($dataobj-raise)] &&  $_first == "" } {
1010            set _first $dataobj
1011        }
1012        set _obj2datasets($dataobj) ""
1013        foreach comp [$dataobj components] {
1014            set tag $dataobj-$comp
1015            if { ![info exists _datasets($tag)] } {
1016                set bytes [$dataobj vtkdata $comp]
1017                set length [string length $bytes]
1018                append _outbuf "dataset add $tag data follows $length\n"
1019                append _outbuf $bytes
1020                set _datasets($tag) 1
1021                SetObjectStyle $dataobj $comp
1022            }
1023            lappend _obj2datasets($dataobj) $tag
1024            if { [info exists _obj2ovride($dataobj-raise)] } {
1025                SendCmd "dataset visible 1 $tag"
1026            } else {
1027                SendCmd "dataset visible 0 $tag"
1028            }
1029            break
1030        }
1031    }
1032    if {"" != $_first} {
1033        set location [$_first hints camera]
1034        if { $location != "" } {
1035            array set view $location
1036        }
1037
1038        if 1 {
1039            # Tell the server the name of the tool, the version, and dataset
1040            # that we are rendering.  Have to do it here because we don't know
1041            # what data objects are using the renderer until be get here.
1042            set args ""
1043            lappend args tool [$_first hints toolId]
1044            lappend args version [$_first hints toolRevision]
1045            lappend args dataset [$_first hints label]
1046            SendCmd "clientinfo [list $args]"
1047        }
1048
1049        foreach axis { x y z } {
1050            set label [$_first hints ${axis}label]
1051            if { $label != "" } {
1052                SendCmd "axis name $axis $label"
1053            }
1054            set units [$_first hints ${axis}units]
1055            if { $units != "" } {
1056                SendCmd "axis units $axis $units"
1057            }
1058        }
1059        array unset _scalarFields
1060        array unset _vectorFields
1061        set _currentField [$_first hints default]
1062        $itk_component(field) choices delete 0 end
1063        $itk_component(fieldmenu) delete 0 end
1064        array unset _fields
1065        foreach { name title units } [$_first hints vectors] {
1066            set _vectorFields($title) $name
1067            $itk_component(field) choices insert end "$name" "$title"
1068            $itk_component(fieldmenu) add radiobutton -label "$title" \
1069                -value $title -variable [itcl::scope _currentField] \
1070                -selectcolor red \
1071                -activebackground black \
1072                -activeforeground white \
1073                -font "Arial 8" \
1074                -command [itcl::code $this Combo invoke]
1075            set _fields($name) [list $title $units]
1076        }
1077        foreach { name title units } [$_first hints scalars] {
1078            set _scalarFields($title) $name
1079            $itk_component(field) choices insert end "$name" "$title"
1080            $itk_component(fieldmenu) add radiobutton -label "$title" \
1081                -value $title -variable [itcl::scope _currentField] \
1082                -selectcolor red \
1083                -activebackground black \
1084                -activeforeground white \
1085                -font "Arial 8" \
1086                -command [itcl::code $this Combo invoke]
1087            set _fields($name) [list $title $units]
1088        }
1089        foreach { name title units } { default Default ??? } {
1090            set _scalarFields($title) $name
1091            $itk_component(field) choices insert end "$name" "$title"
1092            $itk_component(fieldmenu) add radiobutton -label "$title" \
1093                -value $title -variable [itcl::scope _currentField] \
1094                -selectcolor red \
1095                -activebackground black \
1096                -activeforeground white \
1097                -font "Arial 8" \
1098                -command [itcl::code $this Combo invoke]
1099            set _fields($name) [list $title $units]
1100        }
1101        $itk_component(field) value $_currentField
1102    }
1103
1104    InitSettings volume-palette volume-material volume-quality volume-visible \
1105        cutplane-visible \
1106        cutplane-xposition cutplane-yposition cutplane-zposition \
1107        cutplane-xvisible cutplane-yvisible cutplane-zvisible
1108
1109    if { $_reset } {
1110        InitSettings volume-lighting
1111        Zoom reset
1112        set _reset 0
1113    }
1114    set _buffering 0;                        # Turn off buffering.
1115
1116    # Actually write the commands to the server socket.  If it fails, we don't
1117    # care.  We're finished here.
1118    blt::busy hold $itk_component(hull)
1119    sendto $_outbuf;                       
1120    blt::busy release $itk_component(hull)
1121    set _outbuf "";                        # Clear the buffer.               
1122}
1123
1124# ----------------------------------------------------------------------
1125# USAGE: CurrentDatasets ?-all -visible? ?dataobjs?
1126#
1127# Returns a list of server IDs for the current datasets being displayed.  This
1128# is normally a single ID, but it might be a list of IDs if the current data
1129# object has multiple components.
1130# ----------------------------------------------------------------------
1131itcl::body Rappture::VtkVolumeViewer::CurrentDatasets {args} {
1132    set flag [lindex $args 0]
1133    switch -- $flag {
1134        "-all" {
1135            if { [llength $args] > 1 } {
1136                error "CurrentDatasets: can't specify dataobj after \"-all\""
1137            }
1138            set dlist [get -objects]
1139        }
1140        "-visible" {
1141            if { [llength $args] > 1 } {
1142                set dlist {}
1143                set args [lrange $args 1 end]
1144                foreach dataobj $args {
1145                    if { [info exists _obj2ovride($dataobj-raise)] } {
1146                        lappend dlist $dataobj
1147                    }
1148                }
1149            } else {
1150                set dlist [get -visible]
1151            }
1152        }           
1153        default {
1154            set dlist $args
1155        }
1156    }
1157    set rlist ""
1158    foreach dataobj $dlist {
1159        foreach comp [$dataobj components] {
1160            set tag $dataobj-$comp
1161            if { [info exists _datasets($tag)] && $_datasets($tag) } {
1162                lappend rlist $tag
1163            }
1164        }
1165    }
1166    return $rlist
1167}
1168
1169# ----------------------------------------------------------------------
1170# USAGE: Zoom in
1171# USAGE: Zoom out
1172# USAGE: Zoom reset
1173#
1174# Called automatically when the user clicks on one of the zoom
1175# controls for this widget.  Changes the zoom for the current view.
1176# ----------------------------------------------------------------------
1177itcl::body Rappture::VtkVolumeViewer::Zoom {option} {
1178    switch -- $option {
1179        "in" {
1180            set _view(zoom) [expr {$_view(zoom)*1.25}]
1181            SendCmd "camera zoom $_view(zoom)"
1182        }
1183        "out" {
1184            set _view(zoom) [expr {$_view(zoom)*0.8}]
1185            SendCmd "camera zoom $_view(zoom)"
1186        }
1187        "reset" {
1188            array set _view {
1189                qw      0.853553
1190                qx      -0.353553
1191                qy      0.353553
1192                qz      0.146447
1193                zoom    1.0
1194                xpan   0
1195                ypan   0
1196            }
1197            SendCmd "camera reset all"
1198            if { $_first != "" } {
1199                set location [$_first hints camera]
1200                if { $location != "" } {
1201                    array set _view $location
1202                }
1203            }
1204            set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
1205            $_arcball quaternion $q
1206            DoRotate
1207        }
1208    }
1209}
1210
1211itcl::body Rappture::VtkVolumeViewer::PanCamera {} {
1212    set x $_view(xpan)
1213    set y $_view(ypan)
1214    SendCmd "camera pan $x $y"
1215}
1216
1217
1218# ----------------------------------------------------------------------
1219# USAGE: Rotate click <x> <y>
1220# USAGE: Rotate drag <x> <y>
1221# USAGE: Rotate release <x> <y>
1222#
1223# Called automatically when the user clicks/drags/releases in the
1224# plot area.  Moves the plot according to the user's actions.
1225# ----------------------------------------------------------------------
1226itcl::body Rappture::VtkVolumeViewer::Rotate {option x y} {
1227    switch -- $option {
1228        "click" {
1229            $itk_component(view) configure -cursor fleur
1230            set _click(x) $x
1231            set _click(y) $y
1232        }
1233        "drag" {
1234            if {[array size _click] == 0} {
1235                Rotate click $x $y
1236            } else {
1237                set w [winfo width $itk_component(view)]
1238                set h [winfo height $itk_component(view)]
1239                if {$w <= 0 || $h <= 0} {
1240                    return
1241                }
1242
1243                if {[catch {
1244                    # this fails sometimes for no apparent reason
1245                    set dx [expr {double($x-$_click(x))/$w}]
1246                    set dy [expr {double($y-$_click(y))/$h}]
1247                }]} {
1248                    return
1249                }
1250                if { $dx == 0 && $dy == 0 } {
1251                    return
1252                }
1253                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1254                EventuallyRotate $q
1255                set _click(x) $x
1256                set _click(y) $y
1257            }
1258        }
1259        "release" {
1260            Rotate drag $x $y
1261            $itk_component(view) configure -cursor ""
1262            catch {unset _click}
1263        }
1264        default {
1265            error "bad option \"$option\": should be click, drag, release"
1266        }
1267    }
1268}
1269
1270itcl::body Rappture::VtkVolumeViewer::Pick {x y} {
1271    foreach tag [CurrentDatasets -visible] {
1272        SendCmd "dataset getscalar pixel $x $y $tag"
1273    }
1274}
1275
1276# ----------------------------------------------------------------------
1277# USAGE: $this Pan click x y
1278#        $this Pan drag x y
1279#        $this Pan release x y
1280#
1281# Called automatically when the user clicks on one of the zoom
1282# controls for this widget.  Changes the zoom for the current view.
1283# ----------------------------------------------------------------------
1284itcl::body Rappture::VtkVolumeViewer::Pan {option x y} {
1285    switch -- $option {
1286        "set" {
1287            set w [winfo width $itk_component(view)]
1288            set h [winfo height $itk_component(view)]
1289            set x [expr $x / double($w)]
1290            set y [expr $y / double($h)]
1291            set _view(xpan) [expr $_view(xpan) + $x]
1292            set _view(ypan) [expr $_view(ypan) + $y]
1293            PanCamera
1294            return
1295        }
1296        "click" {
1297            set _click(x) $x
1298            set _click(y) $y
1299            $itk_component(view) configure -cursor hand1
1300        }
1301        "drag" {
1302            if { ![info exists _click(x)] } {
1303                set _click(x) $x
1304            }
1305            if { ![info exists _click(y)] } {
1306                set _click(y) $y
1307            }
1308            set w [winfo width $itk_component(view)]
1309            set h [winfo height $itk_component(view)]
1310            set dx [expr ($_click(x) - $x)/double($w)]
1311            set dy [expr ($_click(y) - $y)/double($h)]
1312            set _click(x) $x
1313            set _click(y) $y
1314            set _view(xpan) [expr $_view(xpan) - $dx]
1315            set _view(ypan) [expr $_view(ypan) - $dy]
1316            PanCamera
1317        }
1318        "release" {
1319            Pan drag $x $y
1320            $itk_component(view) configure -cursor ""
1321        }
1322        default {
1323            error "unknown option \"$option\": should set, click, drag, or release"
1324        }
1325    }
1326}
1327
1328# ----------------------------------------------------------------------
1329# USAGE: InitSettings <what> ?<value>?
1330#
1331# Used internally to update rendering settings whenever parameters
1332# change in the popup settings panel.  Sends the new settings off
1333# to the back end.
1334# ----------------------------------------------------------------------
1335itcl::body Rappture::VtkVolumeViewer::InitSettings { args } {
1336    foreach spec $args {
1337        if { [info exists _settings($_first-$spec)] } {
1338            # Reset global setting with dataobj specific setting
1339            set _settings($spec) $_settings($_first-$spec)
1340        }
1341        AdjustSetting $spec
1342    }
1343}
1344
1345#
1346# AdjustSetting --
1347#
1348#       Changes/updates a specific setting in the widget.  There are
1349#       usually user-setable option.  Commands are sent to the render
1350#       server.
1351#
1352itcl::body Rappture::VtkVolumeViewer::AdjustSetting {what {value ""}} {
1353    if { ![isconnected] } {
1354        return
1355    }
1356    switch -- $what {
1357        "volume-visible" {
1358            set bool $_settings(volume-visible)
1359            foreach dataset [CurrentDatasets -visible] {
1360                SendCmd "volume visible $bool $dataset"
1361            }
1362            if { $bool } {
1363                Rappture::Tooltip::for $itk_component(volume) \
1364                    "Hide the volume"
1365            } else {
1366                Rappture::Tooltip::for $itk_component(volume) \
1367                    "Show the volume"
1368            }
1369        }
1370        "volume-material" {
1371            set val $_settings(volume-material)
1372            set diffuse [expr {0.01*$val}]
1373            set specular [expr {0.01*$val}]
1374            #set power [expr {sqrt(160*$val+1.0)}]
1375            set power [expr {$val+1.0}]
1376            foreach dataset [CurrentDatasets -visible] {
1377                SendCmd "volume shading diffuse $diffuse $dataset"
1378                SendCmd "volume shading specular $specular $power $dataset"
1379            }
1380        }
1381        "volume-lighting" {
1382            set bool $_settings(volume-lighting)
1383            foreach dataset [CurrentDatasets -visible] {
1384                SendCmd "volume lighting $bool $dataset"
1385            }
1386        }
1387        "volume-quality" {
1388            set val $_settings(volume-quality)
1389            set val [expr {0.01*$val}]
1390            foreach dataset [CurrentDatasets -visible] {
1391                SendCmd "volume quality $val $dataset"
1392            }
1393        }
1394        "axis-visible" {
1395            set bool $_settings(axis-visible)
1396            SendCmd "axis visible all $bool"
1397        }
1398        "axis-labels" {
1399            set bool $_settings(axis-labels)
1400            SendCmd "axis labels all $bool"
1401        }
1402        "axis-xgrid" - "axis-ygrid" - "axis-zgrid" {
1403            set axis [string range $what 5 5]
1404            set bool $_settings($what)
1405            SendCmd "axis grid $axis $bool"
1406        }
1407        "axis-mode" {
1408            set mode [$itk_component(axismode) value]
1409            set mode [$itk_component(axismode) translate $mode]
1410            set _settings($what) $mode
1411            SendCmd "axis flymode $mode"
1412        }
1413        "cutplane-edges" {
1414            set bool $_settings($what)
1415            foreach dataset [CurrentDatasets -visible] {
1416                SendCmd "cutplane edges $bool $dataset"
1417            }
1418        }
1419        "cutplane-visible" {
1420            set bool $_settings($what)
1421            foreach dataset [CurrentDatasets -visible] {
1422                SendCmd "cutplane visible $bool $dataset"
1423            }
1424        }
1425        "cutplane-wireframe" {
1426            set bool $_settings($what)
1427            foreach dataset [CurrentDatasets -visible] {
1428                SendCmd "cutplane wireframe $bool $dataset"
1429            }
1430        }
1431        "cutplane-lighting" {
1432            set bool $_settings($what)
1433            foreach dataset [CurrentDatasets -visible] {
1434                SendCmd "cutplane lighting $bool $dataset"
1435            }
1436        }
1437        "cutplane-opacity" {
1438            set val $_settings($what)
1439            set sval [expr { 0.01 * double($val) }]
1440            foreach dataset [CurrentDatasets -visible] {
1441                SendCmd "cutplane opacity $sval $dataset"
1442            }
1443        }
1444        "cutplane-xvisible" - "cutplane-yvisible" - "cutplane-zvisible" {
1445            set axis [string range $what 9 9]
1446            set bool $_settings($what)
1447            if { $bool } {
1448                $itk_component(${axis}CutScale) configure -state normal \
1449                    -troughcolor white
1450            } else {
1451                $itk_component(${axis}CutScale) configure -state disabled \
1452                    -troughcolor grey82
1453            }
1454            foreach dataset [CurrentDatasets -visible] {
1455                SendCmd "cutplane axis $axis $bool $dataset"
1456            }
1457        }
1458        "cutplane-xposition" - "cutplane-yposition" - "cutplane-zposition" {
1459            set axis [string range $what 9 9]
1460            set pos [expr $_settings($what) * 0.01]
1461            foreach dataset [CurrentDatasets -visible] {
1462                SendCmd "cutplane slice ${axis} ${pos} $dataset"
1463            }
1464            set _cutplanePending 0
1465        }
1466        "volume-palette" {
1467            set palette [$itk_component(palette) value]
1468            set _settings(volume-palette) $palette
1469            foreach dataset [CurrentDatasets -visible $_first] {
1470                foreach {dataobj comp} [split $dataset -] break
1471                ChangeColormap $dataobj $comp $palette
1472            }
1473            set _legendPending 1
1474        }
1475        "volume-palette" {
1476            set palette [$itk_component(palette) value]
1477            set _settings(volume-palette) $palette
1478            foreach dataset [CurrentDatasets -visible $_first] {
1479                foreach {dataobj comp} [split $dataset -] break
1480                ChangeColormap $dataobj $comp $palette
1481            }
1482            set _legendPending 1
1483        }
1484        "volume-field" {
1485            set new [$itk_component(field) value]
1486            set value [$itk_component(field) translate $new]
1487            set _settings(volume-field) $value
1488            if { [info exists _scalarFields($new)] } {
1489                set name $_scalarFields($new)
1490                set _colorMode scalar
1491                set _currentField $new
1492            } elseif { [info exists _vectorFields($new)] } {
1493                set name $_vectorFields($new)
1494                set _colorMode vmag
1495                set _currentField $new
1496            } else {
1497                puts stderr "unknown field \"$new\""
1498                return
1499            }
1500            foreach dataset [CurrentDatasets -visible] {
1501                #puts stderr "volume colormode $_colorMode ${name} $dataset"
1502                puts stderr "cutplane colormode $_colorMode ${name} $dataset"
1503                #SendCmd "volume colormode $_colorMode ${name} $dataset"
1504                SendCmd "cutplane colormode $_colorMode ${name} $dataset"
1505            }
1506            set _legendPending 1
1507        }
1508        default {
1509            error "don't know how to fix $what"
1510        }
1511    }
1512}
1513
1514#
1515# RequestLegend --
1516#
1517#       Request a new legend from the server.  The size of the legend
1518#       is determined from the height of the canvas.  It will be rotated
1519#       to be vertical when drawn.
1520#
1521itcl::body Rappture::VtkVolumeViewer::RequestLegend {} {
1522    set font "Arial 8"
1523    set lineht [font metrics $font -linespace]
1524    set c $itk_component(legend)
1525    set w 12
1526    set h [expr {$_height - 3 * ($lineht + 2)}]
1527    if { $h < 1} {
1528        return
1529    }
1530    if { [info exists _scalarFields($_currentField)] } {
1531        set name $_scalarFields($_currentField)
1532    } elseif { [info exists _vectorFields($_currentField)] } {
1533        set name $_vectorFields($_currentField)
1534    } else {
1535        return
1536    }
1537    # Set the legend on the first volume dataset.
1538    foreach dataset [CurrentDatasets -visible $_first] {
1539        foreach {dataobj comp} [split $dataset -] break
1540        if { [info exists _dataset2style($dataset)] } {
1541            SendCmdNoWait \
1542                "legend $_dataset2style($dataset) $_colorMode $name {} $w $h 0"
1543            break;
1544        }
1545    }
1546}
1547
1548#
1549# ChangeColormap --
1550#
1551itcl::body Rappture::VtkVolumeViewer::ChangeColormap {dataobj comp color} {
1552    set tag $dataobj-$comp
1553    if { ![info exist _style($tag)] } {
1554        error "no initial colormap"
1555    }
1556    array set style $_style($tag)
1557    set style(-color) $color
1558    set _style($tag) [array get style]
1559    SetColormap $dataobj $comp
1560}
1561
1562#
1563# SetColormap --
1564#
1565itcl::body Rappture::VtkVolumeViewer::SetColormap { dataobj comp } {
1566    array set style {
1567        -color BGYOR
1568        -levels 6
1569        -opacity 1.0
1570    }
1571    set tag $dataobj-$comp
1572    if { ![info exists _initialStyle($tag)] } {
1573        # Save the initial component style.
1574        set _initialStyle($tag) [$dataobj style $comp]
1575    }
1576
1577    # Override defaults with initial style defined in xml.
1578    array set style $_initialStyle($tag)
1579
1580    if { ![info exists _style($tag)] } {
1581        set _style($tag) [array get style]
1582    }
1583    # Override initial style with current style.
1584    array set style $_style($tag)
1585
1586    set name "$style(-color):$style(-levels):$style(-opacity)"
1587    if { ![info exists _colormaps($name)] } {
1588        BuildColormap $name [array get style]
1589        set _colormaps($name) 1
1590    }
1591    if { ![info exists _dataset2style($tag)] ||
1592         $_dataset2style($tag) != $name } {
1593        SendCmd "volume colormap $name $tag"
1594        SendCmd "cutplane colormap $name-opaque $tag"
1595        set _dataset2style($tag) $name
1596    }
1597}
1598
1599itcl::body Rappture::VtkVolumeViewer::ColorsToColormap { colors } {
1600puts stderr colors=$colors
1601    switch -- $colors {
1602        "grey-to-blue" {
1603            return {
1604                0.0                      0.200 0.200 0.200
1605                0.14285714285714285      0.400 0.400 0.400
1606                0.2857142857142857       0.600 0.600 0.600
1607                0.42857142857142855      0.900 0.900 0.900
1608                0.5714285714285714       0.800 1.000 1.000
1609                0.7142857142857143       0.600 1.000 1.000
1610                0.8571428571428571       0.400 0.900 1.000
1611                1.0                      0.000 0.600 0.800
1612            }
1613        }
1614        "blue-to-grey" {
1615            return {
1616                0.0                     0.000 0.600 0.800
1617                0.14285714285714285     0.400 0.900 1.000
1618                0.2857142857142857      0.600 1.000 1.000
1619                0.42857142857142855     0.800 1.000 1.000
1620                0.5714285714285714      0.900 0.900 0.900
1621                0.7142857142857143      0.600 0.600 0.600
1622                0.8571428571428571      0.400 0.400 0.400
1623                1.0                     0.200 0.200 0.200
1624            }
1625        }
1626        "blue" {
1627            return {
1628                0.0                     0.900 1.000 1.000
1629                0.1111111111111111      0.800 0.983 1.000
1630                0.2222222222222222      0.700 0.950 1.000
1631                0.3333333333333333      0.600 0.900 1.000
1632                0.4444444444444444      0.500 0.833 1.000
1633                0.5555555555555556      0.400 0.750 1.000
1634                0.6666666666666666      0.300 0.650 1.000
1635                0.7777777777777778      0.200 0.533 1.000
1636                0.8888888888888888      0.100 0.400 1.000
1637                1.0                     0.000 0.250 1.000
1638            }
1639        }
1640        "brown-to-blue" {
1641            return {
1642                0.0                             0.200   0.100   0.000
1643                0.09090909090909091             0.400   0.187   0.000
1644                0.18181818181818182             0.600   0.379   0.210
1645                0.2727272727272727              0.800   0.608   0.480
1646                0.36363636363636365             0.850   0.688   0.595
1647                0.45454545454545453             0.950   0.855   0.808
1648                0.5454545454545454              0.800   0.993   1.000
1649                0.6363636363636364              0.600   0.973   1.000
1650                0.7272727272727273              0.400   0.940   1.000
1651                0.8181818181818182              0.200   0.893   1.000
1652                0.9090909090909091              0.000   0.667   0.800
1653                1.0                             0.000   0.480   0.600
1654            }
1655        }
1656        "blue-to-brown" {
1657            return {
1658                0.0                             0.000   0.480   0.600
1659                0.09090909090909091             0.000   0.667   0.800
1660                0.18181818181818182             0.200   0.893   1.000
1661                0.2727272727272727              0.400   0.940   1.000
1662                0.36363636363636365             0.600   0.973   1.000
1663                0.45454545454545453             0.800   0.993   1.000
1664                0.5454545454545454              0.950   0.855   0.808
1665                0.6363636363636364              0.850   0.688   0.595
1666                0.7272727272727273              0.800   0.608   0.480
1667                0.8181818181818182              0.600   0.379   0.210
1668                0.9090909090909091              0.400   0.187   0.000
1669                1.0                             0.200   0.100   0.000
1670            }
1671        }
1672        "blue-to-orange" {
1673            return {
1674                0.0                             0.000   0.167   1.000
1675                0.09090909090909091             0.100   0.400   1.000
1676                0.18181818181818182             0.200   0.600   1.000
1677                0.2727272727272727              0.400   0.800   1.000
1678                0.36363636363636365             0.600   0.933   1.000
1679                0.45454545454545453             0.800   1.000   1.000
1680                0.5454545454545454              1.000   1.000   0.800
1681                0.6363636363636364              1.000   0.933   0.600
1682                0.7272727272727273              1.000   0.800   0.400
1683                0.8181818181818182              1.000   0.600   0.200
1684                0.9090909090909091              1.000   0.400   0.100
1685                1.0                             1.000   0.167   0.000
1686            }
1687        }
1688        "orange-to-blue" {
1689            return {
1690                0.0                             1.000   0.167   0.000
1691                0.09090909090909091             1.000   0.400   0.100
1692                0.18181818181818182             1.000   0.600   0.200
1693                0.2727272727272727              1.000   0.800   0.400
1694                0.36363636363636365             1.000   0.933   0.600
1695                0.45454545454545453             1.000   1.000   0.800
1696                0.5454545454545454              0.800   1.000   1.000
1697                0.6363636363636364              0.600   0.933   1.000
1698                0.7272727272727273              0.400   0.800   1.000
1699                0.8181818181818182              0.200   0.600   1.000
1700                0.9090909090909091              0.100   0.400   1.000
1701                1.0                             0.000   0.167   1.000
1702            }
1703        }
1704        "rainbow" {
1705            set clist {
1706                "#EE82EE"
1707                "#4B0082"
1708                "blue"
1709                "#008000"
1710                "yellow"
1711                "#FFA500"
1712                "red"
1713            }
1714        }
1715        "BGYOR" {
1716            set clist {
1717                "blue"
1718                "#008000"
1719                "yellow"
1720                "#FFA500"
1721                "red"
1722            }
1723        }
1724        "ROYGB" {
1725            set clist {
1726                "red"
1727                "#FFA500"
1728                "yellow"
1729                "#008000"
1730                "blue"
1731            }
1732        }
1733        "RYGCB" {
1734            set clist {
1735                "red"
1736                "yellow"
1737                "green"
1738                "cyan"
1739                "blue"
1740            }
1741        }
1742        "BCGYR" {
1743            set clist {
1744                "blue"
1745                "cyan"
1746                "green"
1747                "yellow"
1748                "red"
1749            }
1750        }
1751        "spectral" {
1752            return {
1753                0.0 0.150 0.300 1.000
1754                0.1 0.250 0.630 1.000
1755                0.2 0.450 0.850 1.000
1756                0.3 0.670 0.970 1.000
1757                0.4 0.880 1.000 1.000
1758                0.5 1.000 1.000 0.750
1759                0.6 1.000 0.880 0.600
1760                0.7 1.000 0.680 0.450
1761                0.8 0.970 0.430 0.370
1762                0.9 0.850 0.150 0.196
1763                1.0 0.650 0.000 0.130
1764            }
1765        }
1766        "green-to-magenta" {
1767            return {
1768                0.0 0.000 0.316 0.000
1769                0.06666666666666667 0.000 0.526 0.000
1770                0.13333333333333333 0.000 0.737 0.000
1771                0.2 0.000 0.947 0.000
1772                0.26666666666666666 0.316 1.000 0.316
1773                0.3333333333333333 0.526 1.000 0.526
1774                0.4 0.737 1.000 0.737
1775                0.4666666666666667 1.000 1.000 1.000
1776                0.5333333333333333 1.000 0.947 1.000
1777                0.6 1.000 0.737 1.000
1778                0.6666666666666666 1.000 0.526 1.000
1779                0.7333333333333333 1.000 0.316 1.000
1780                0.8 0.947 0.000 0.947
1781                0.8666666666666667 0.737 0.000 0.737
1782                0.9333333333333333 0.526 0.000 0.526
1783                1.0 0.316 0.000 0.316
1784            }
1785        }
1786        "greyscale" {
1787            return {
1788                0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0
1789            }
1790        }
1791        "nanohub" {
1792            set clist "white yellow green cyan blue magenta"
1793        }
1794        default {
1795            set clist [split $colors :]
1796        }
1797    }
1798    set cmap {}
1799    set nColors [llength $clist]
1800    if { $nColors == 1 } {
1801        append cmap "0.0 [Color2RGB $colors] "
1802        append cmap "1.0 [Color2RGB $colors] "
1803    } else {
1804        for {set i 0} {$i < [llength $clist]} {incr i} {
1805            set x [expr {double($i)/([llength $clist]-1)}]
1806            set color [lindex $clist $i]
1807            append cmap "$x [Color2RGB $color] "
1808        }
1809    }
1810    return $cmap
1811}
1812
1813#
1814# BuildColormap --
1815#
1816itcl::body Rappture::VtkVolumeViewer::BuildColormap { name styles } {
1817    array set style $styles
1818    set cmap [ColorsToColormap $style(-color)]
1819    if { [llength $cmap] == 0 } {
1820        set cmap "0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0"
1821    }
1822    if { ![info exists _settings(volume-opacity)] } {
1823        set _settings(volume-opacity) $style(-opacity)
1824    }
1825    set max $_settings(volume-opacity)
1826
1827    set opaqueWmap "0.0 1.0 1.0 1.0"
1828    #set wmap "0.0 0.0 0.1 0.0 0.2 0.8 0.98 0.8 0.99 0.0 1.0 0.0"
1829    # Approximate cubic opacity curve
1830    set wmap "0.0 0.0 0.1 0.001 0.2 0.008 0.3 0.027 0.4 0.064 0.5 0.125 0.6 0.216 0.7 0.343 0.8 0.512 0.9 0.729 1.0 1.0"
1831    SendCmd "colormap add $name { $cmap } { $wmap }"
1832    SendCmd "colormap add $name-opaque { $cmap } { $opaqueWmap }"
1833}
1834
1835# ----------------------------------------------------------------------
1836# CONFIGURATION OPTION: -plotbackground
1837# ----------------------------------------------------------------------
1838itcl::configbody Rappture::VtkVolumeViewer::plotbackground {
1839    if { [isconnected] } {
1840        foreach {r g b} [Color2RGB $itk_option(-plotbackground)] break
1841        SendCmd "screen bgcolor $r $g $b"
1842    }
1843}
1844
1845# ----------------------------------------------------------------------
1846# CONFIGURATION OPTION: -plotforeground
1847# ----------------------------------------------------------------------
1848itcl::configbody Rappture::VtkVolumeViewer::plotforeground {
1849    if { [isconnected] } {
1850        foreach {r g b} [Color2RGB $itk_option(-plotforeground)] break
1851        #fix this!
1852        #SendCmd "color background $r $g $b"
1853    }
1854}
1855
1856itcl::body Rappture::VtkVolumeViewer::limits { dataobj } {
1857    return
1858    array unset _limits $dataobj-*
1859    foreach comp [$dataobj components] {
1860        set tag $dataobj-$comp
1861        if { ![info exists _limits($tag)] } {
1862            set data [$dataobj blob $comp]
1863            set tmpfile file[pid].vtk
1864            set f [open "$tmpfile" "w"]
1865            fconfigure $f -translation binary -encoding binary
1866            puts $f $data
1867            close $f
1868            set reader [vtkDataSetReader $tag-xvtkDataSetReader]
1869            $reader SetFileName $tmpfile
1870            $reader ReadAllScalarsOn
1871            $reader ReadAllVectorsOn
1872            $reader ReadAllFieldsOn
1873            $reader Update
1874            set output [$reader GetOutput]
1875            set _limits($tag) [$output GetBounds]
1876            set pointData [$output GetPointData]
1877            puts stderr "\#scalars=[$reader GetNumberOfScalarsInFile]"
1878            puts stderr "\#fielddata=[$reader GetNumberOfFieldDataInFile]"
1879            puts stderr "fielddataname=[$reader GetFieldDataNameInFile 0]"
1880            set fieldData [$output GetFieldData]
1881            set pointData [$output GetPointData]
1882            puts stderr "field \#arrays=[$fieldData GetNumberOfArrays]"
1883            for { set i 0 } { $i < [$fieldData GetNumberOfArrays] } { incr i } {
1884                puts stderr [$fieldData GetArrayName $i]
1885            }
1886            puts stderr "point \#arrays=[$pointData GetNumberOfArrays]"
1887            for { set i 0 } { $i < [$pointData GetNumberOfArrays] } { incr i } {
1888                set name [$pointData GetArrayName $i]
1889                if { ![info exists _fields($name)] } {
1890                    $itk_component(field) choices insert end "$name" "$name"
1891                    set _fields($name) 1
1892                }
1893            }
1894            puts stderr "field \#components=[$fieldData GetNumberOfComponents]"
1895            puts stderr "point \#components=[$pointData GetNumberOfComponents]"
1896            puts stderr "field \#tuples=[$fieldData GetNumberOfTuples]"
1897            puts stderr "point \#tuples=[$pointData GetNumberOfTuples]"
1898            puts stderr "point \#scalars=[$pointData GetScalars]"
1899            puts stderr vectors=[$pointData GetVectors]
1900            rename $output ""
1901            rename $reader ""
1902            file delete $tmpfile
1903        }
1904        foreach { xMin xMax yMin yMax zMin zMax} $_limits($tag) break
1905        if {![info exists limits(xmin)] || $limits(xmin) > $xMin} {
1906            set limits(xmin) $xMin
1907        }
1908        if {![info exists limits(xmax)] || $limits(xmax) < $xMax} {
1909            set limits(xmax) $xMax
1910        }
1911        if {![info exists limits(ymin)] || $limits(ymin) > $yMin} {
1912            set limits(ymin) $xMin
1913        }
1914        if {![info exists limits(ymax)] || $limits(ymax) < $yMax} {
1915            set limits(ymax) $yMax
1916        }
1917        if {![info exists limits(zmin)] || $limits(zmin) > $zMin} {
1918            set limits(zmin) $zMin
1919        }
1920        if {![info exists limits(zmax)] || $limits(zmax) < $zMax} {
1921            set limits(zmax) $zMax
1922        }
1923    }
1924    return [array get limits]
1925}
1926
1927itcl::body Rappture::VtkVolumeViewer::BuildVolumeTab {} {
1928
1929    set fg [option get $itk_component(hull) font Font]
1930    #set bfg [option get $itk_component(hull) boldFont Font]
1931
1932    set inner [$itk_component(main) insert end \
1933        -title "Volume Settings" \
1934        -icon [Rappture::icon volume-on]]
1935    $inner configure -borderwidth 4
1936
1937    checkbutton $inner.volume \
1938        -text "Show Volume" \
1939        -variable [itcl::scope _settings(volume-visible)] \
1940        -command [itcl::code $this AdjustSetting volume-visible] \
1941        -font "Arial 9"
1942
1943    checkbutton $inner.lighting \
1944        -text "Enable Lighting" \
1945        -variable [itcl::scope _settings(volume-lighting)] \
1946        -command [itcl::code $this AdjustSetting volume-lighting] \
1947        -font "Arial 9"
1948
1949    label $inner.dim_l -text "Dim" -font "Arial 9"
1950    ::scale $inner.material -from 0 -to 100 -orient horizontal \
1951        -variable [itcl::scope _settings(volume-material)] \
1952        -width 10 \
1953        -showvalue off -command [itcl::code $this AdjustSetting volume-material]
1954    label $inner.bright_l -text "Bright" -font "Arial 9"
1955
1956    label $inner.opacity_l -text "Opacity" -font "Arial 9"
1957    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1958        -variable [itcl::scope _settings(volume-opacity)] \
1959        -width 10 \
1960        -showvalue off \
1961        -command [itcl::code $this AdjustSetting volume-opacity]
1962
1963    label $inner.quality_l -text "Quality" -font "Arial 9"
1964    ::scale $inner.quality -from 0 -to 100 -orient horizontal \
1965        -variable [itcl::scope _settings(volume-quality)] \
1966        -width 10 \
1967        -showvalue off -command [itcl::code $this AdjustSetting volume-quality]
1968
1969    label $inner.field_l -text "Field" -font "Arial 9"
1970    itk_component add field {
1971        Rappture::Combobox $inner.field -width 10 -editable no
1972    }
1973    bind $inner.field <<Value>> \
1974        [itcl::code $this AdjustSetting volume-field]
1975
1976    label $inner.palette_l -text "Palette" -font "Arial 9"
1977    itk_component add palette {
1978        Rappture::Combobox $inner.palette -width 10 -editable no
1979    }
1980    $inner.palette choices insert end \
1981        "BCGYR"              "BCGYR"            \
1982        "BGYOR"              "BGYOR"            \
1983        "blue"               "blue"             \
1984        "blue-to-brown"      "blue-to-brown"    \
1985        "blue-to-orange"     "blue-to-orange"   \
1986        "blue-to-grey"       "blue-to-grey"     \
1987        "green-to-magenta"   "green-to-magenta" \
1988        "greyscale"          "greyscale"        \
1989        "nanohub"            "nanohub"          \
1990        "rainbow"            "rainbow"          \
1991        "spectral"           "spectral"         \
1992        "ROYGB"              "ROYGB"            \
1993        "RYGCB"              "RYGCB"            \
1994        "brown-to-blue"      "brown-to-blue"    \
1995        "grey-to-blue"       "grey-to-blue"     \
1996        "orange-to-blue"     "orange-to-blue"   
1997
1998    $itk_component(palette) value "nanohub"
1999    bind $inner.palette <<Value>> \
2000        [itcl::code $this AdjustSetting volume-palette]
2001
2002    blt::table $inner \
2003        0,0 $inner.volume    -anchor w -pady 2 -cspan 4 \
2004        2,0 $inner.lighting  -anchor w -pady 2 -cspan 4 \
2005        3,0 $inner.dim_l     -anchor e -pady 2 \
2006        3,1 $inner.material  -fill x   -pady 2 \
2007        3,2 $inner.bright_l  -anchor w -pady 2 \
2008        4,0 $inner.quality_l -anchor w -pady 2 -cspan 2 \
2009        5,0 $inner.quality   -fill x   -pady 2 -cspan 2 \
2010        6,0 $inner.field_l   -anchor w -pady 2  \
2011        6,1 $inner.field     -anchor w -pady 2 -cspan 2 \
2012        7,0 $inner.palette_l -anchor w -pady 2  \
2013        7,1 $inner.palette   -anchor w -pady 2 -cspan 2 \
2014
2015    blt::table configure $inner r* c* -resize none
2016    blt::table configure $inner r8 c3 -resize expand
2017}
2018
2019itcl::body Rappture::VtkVolumeViewer::BuildAxisTab {} {
2020
2021    set fg [option get $itk_component(hull) font Font]
2022    #set bfg [option get $itk_component(hull) boldFont Font]
2023
2024    set inner [$itk_component(main) insert end \
2025        -title "Axis Settings" \
2026        -icon [Rappture::icon axis1]]
2027    $inner configure -borderwidth 4
2028
2029    checkbutton $inner.visible \
2030        -text "Show Axes" \
2031        -variable [itcl::scope _settings(axis-visible)] \
2032        -command [itcl::code $this AdjustSetting axis-visible] \
2033        -font "Arial 9"
2034
2035    checkbutton $inner.labels \
2036        -text "Show Axis Labels" \
2037        -variable [itcl::scope _settings(axis-labels)] \
2038        -command [itcl::code $this AdjustSetting axis-labels] \
2039        -font "Arial 9"
2040
2041    checkbutton $inner.gridx \
2042        -text "Show X Grid" \
2043        -variable [itcl::scope _settings(axis-xgrid)] \
2044        -command [itcl::code $this AdjustSetting axis-xgrid] \
2045        -font "Arial 9"
2046    checkbutton $inner.gridy \
2047        -text "Show Y Grid" \
2048        -variable [itcl::scope _settings(axis-ygrid)] \
2049        -command [itcl::code $this AdjustSetting axis-ygrid] \
2050        -font "Arial 9"
2051    checkbutton $inner.gridz \
2052        -text "Show Z Grid" \
2053        -variable [itcl::scope _settings(axis-zgrid)] \
2054        -command [itcl::code $this AdjustSetting axis-zgrid] \
2055        -font "Arial 9"
2056
2057    label $inner.mode_l -text "Mode" -font "Arial 9"
2058
2059    itk_component add axismode {
2060        Rappture::Combobox $inner.mode -width 10 -editable no
2061    }
2062    $inner.mode choices insert end \
2063        "static_triad"    "static" \
2064        "closest_triad"   "closest" \
2065        "furthest_triad"  "furthest" \
2066        "outer_edges"     "outer"         
2067    $itk_component(axismode) value "static"
2068    bind $inner.mode <<Value>> [itcl::code $this AdjustSetting axis-mode]
2069
2070    blt::table $inner \
2071        0,0 $inner.visible -anchor w -cspan 2 \
2072        1,0 $inner.labels  -anchor w -cspan 2 \
2073        2,0 $inner.gridx   -anchor w -cspan 2 \
2074        3,0 $inner.gridy   -anchor w -cspan 2 \
2075        4,0 $inner.gridz   -anchor w -cspan 2 \
2076        5,0 $inner.mode_l  -anchor w -cspan 2 -padx { 2 0 } \
2077        6,0 $inner.mode    -fill x   -cspan 2
2078
2079    blt::table configure $inner r* c* -resize none
2080    blt::table configure $inner r7 c1 -resize expand
2081}
2082
2083
2084itcl::body Rappture::VtkVolumeViewer::BuildCameraTab {} {
2085    set inner [$itk_component(main) insert end \
2086        -title "Camera Settings" \
2087        -icon [Rappture::icon camera]]
2088    $inner configure -borderwidth 4
2089
2090    set labels { qx qy qz qw xpan ypan zoom }
2091    set row 0
2092    foreach tag $labels {
2093        label $inner.${tag}label -text $tag -font "Arial 9"
2094        entry $inner.${tag} -font "Arial 9"  -bg white \
2095            -textvariable [itcl::scope _view($tag)]
2096        bind $inner.${tag} <KeyPress-Return> \
2097            [itcl::code $this camera set ${tag}]
2098        blt::table $inner \
2099            $row,0 $inner.${tag}label -anchor e -pady 2 \
2100            $row,1 $inner.${tag} -anchor w -pady 2
2101        blt::table configure $inner r$row -resize none
2102        incr row
2103    }
2104    checkbutton $inner.ortho \
2105        -text "Orthographic Projection" \
2106        -variable [itcl::scope _view(ortho)] \
2107        -command [itcl::code $this camera set ortho] \
2108        -font "Arial 9"
2109    blt::table $inner \
2110            $row,0 $inner.ortho -columnspan 2 -anchor w -pady 2
2111    blt::table configure $inner r$row -resize none
2112    incr row
2113
2114    blt::table configure $inner c0 c1 -resize none
2115    blt::table configure $inner c2 -resize expand
2116    blt::table configure $inner r$row -resize expand
2117}
2118
2119itcl::body Rappture::VtkVolumeViewer::BuildCutplaneTab {} {
2120
2121    set fg [option get $itk_component(hull) font Font]
2122   
2123    set inner [$itk_component(main) insert end \
2124        -title "Cutplane Settings" \
2125        -icon [Rappture::icon cutbutton]]
2126
2127    $inner configure -borderwidth 4
2128
2129    checkbutton $inner.visible \
2130        -text "Show Cutplanes" \
2131        -variable [itcl::scope _settings(cutplane-visible)] \
2132        -command [itcl::code $this AdjustSetting cutplane-visible] \
2133        -font "Arial 9"
2134
2135    checkbutton $inner.wireframe \
2136        -text "Show Wireframe" \
2137        -variable [itcl::scope _settings(cutplane-wireframe)] \
2138        -command [itcl::code $this AdjustSetting cutplane-wireframe] \
2139        -font "Arial 9"
2140
2141    checkbutton $inner.lighting \
2142        -text "Enable Lighting" \
2143        -variable [itcl::scope _settings(cutplane-lighting)] \
2144        -command [itcl::code $this AdjustSetting cutplane-lighting] \
2145        -font "Arial 9"
2146
2147    checkbutton $inner.edges \
2148        -text "Show Edges" \
2149        -variable [itcl::scope _settings(cutplane-edges)] \
2150        -command [itcl::code $this AdjustSetting cutplane-edges] \
2151        -font "Arial 9"
2152
2153    label $inner.opacity_l -text "Opacity" -font "Arial 9"
2154    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
2155        -variable [itcl::scope _settings(cutplane-opacity)] \
2156        -width 10 \
2157        -showvalue off \
2158        -command [itcl::code $this AdjustSetting cutplane-opacity]
2159    $inner.opacity set $_settings(cutplane-opacity)
2160
2161    # X-value slicer...
2162    itk_component add xCutButton {
2163        Rappture::PushButton $inner.xbutton \
2164            -onimage [Rappture::icon x-cutplane] \
2165            -offimage [Rappture::icon x-cutplane] \
2166            -command [itcl::code $this AdjustSetting cutplane-xvisible] \
2167            -variable [itcl::scope _settings(cutplane-xvisible)]
2168    }
2169    Rappture::Tooltip::for $itk_component(xCutButton) \
2170        "Toggle the X-axis cutplane on/off"
2171
2172    itk_component add xCutScale {
2173        ::scale $inner.xval -from 100 -to 1 \
2174            -width 10 -orient vertical -showvalue yes \
2175            -borderwidth 1 -highlightthickness 0 \
2176            -command [itcl::code $this EventuallySetCutplane x] \
2177            -variable [itcl::scope _settings(cutplane-xposition)]
2178    } {
2179        usual
2180        ignore -borderwidth -highlightthickness
2181    }
2182    # Set the default cutplane value before disabling the scale.
2183    $itk_component(xCutScale) set 50
2184    $itk_component(xCutScale) configure -state disabled
2185    Rappture::Tooltip::for $itk_component(xCutScale) \
2186        "@[itcl::code $this Slice tooltip x]"
2187
2188    # Y-value slicer...
2189    itk_component add yCutButton {
2190        Rappture::PushButton $inner.ybutton \
2191            -onimage [Rappture::icon y-cutplane] \
2192            -offimage [Rappture::icon y-cutplane] \
2193            -command [itcl::code $this AdjustSetting cutplane-yvisible] \
2194            -variable [itcl::scope _settings(cutplane-yvisible)]
2195    }
2196    Rappture::Tooltip::for $itk_component(yCutButton) \
2197        "Toggle the Y-axis cutplane on/off"
2198
2199    itk_component add yCutScale {
2200        ::scale $inner.yval -from 100 -to 1 \
2201            -width 10 -orient vertical -showvalue yes \
2202            -borderwidth 1 -highlightthickness 0 \
2203            -command [itcl::code $this EventuallySetCutplane y] \
2204            -variable [itcl::scope _settings(cutplane-yposition)]
2205    } {
2206        usual
2207        ignore -borderwidth -highlightthickness
2208    }
2209    Rappture::Tooltip::for $itk_component(yCutScale) \
2210        "@[itcl::code $this Slice tooltip y]"
2211    # Set the default cutplane value before disabling the scale.
2212    $itk_component(yCutScale) set 50
2213    $itk_component(yCutScale) configure -state disabled
2214
2215    # Z-value slicer...
2216    itk_component add zCutButton {
2217        Rappture::PushButton $inner.zbutton \
2218            -onimage [Rappture::icon z-cutplane] \
2219            -offimage [Rappture::icon z-cutplane] \
2220            -command [itcl::code $this AdjustSetting cutplane-zvisible] \
2221            -variable [itcl::scope _settings(cutplane-zvisible)]
2222    }
2223    Rappture::Tooltip::for $itk_component(zCutButton) \
2224        "Toggle the Z-axis cutplane on/off"
2225
2226    itk_component add zCutScale {
2227        ::scale $inner.zval -from 100 -to 1 \
2228            -width 10 -orient vertical -showvalue yes \
2229            -borderwidth 1 -highlightthickness 0 \
2230            -command [itcl::code $this EventuallySetCutplane z] \
2231            -variable [itcl::scope _settings(cutplane-zposition)]
2232    } {
2233        usual
2234        ignore -borderwidth -highlightthickness
2235    }
2236    $itk_component(zCutScale) set 50
2237    $itk_component(zCutScale) configure -state disabled
2238    #$itk_component(zCutScale) configure -state disabled
2239    Rappture::Tooltip::for $itk_component(zCutScale) \
2240        "@[itcl::code $this Slice tooltip z]"
2241
2242    blt::table $inner \
2243        0,0 $inner.visible              -anchor w -pady 2 -cspan 4 \
2244        1,0 $inner.lighting             -anchor w -pady 2 -cspan 4 \
2245        2,0 $inner.wireframe            -anchor w -pady 2 -cspan 4 \
2246        3,0 $inner.edges                -anchor w -pady 2 -cspan 4 \
2247        4,0 $inner.opacity_l            -anchor w -pady 2 -cspan 3 \
2248        5,0 $inner.opacity              -fill x   -pady 2 -cspan 3 \
2249        6,0 $itk_component(xCutButton)  -anchor e -padx 2 -pady 2 \
2250        7,0 $itk_component(xCutScale)   -fill y \
2251        6,1 $itk_component(yCutButton)  -anchor e -padx 2 -pady 2 \
2252        7,1 $itk_component(yCutScale)   -fill y \
2253        6,2 $itk_component(zCutButton)  -anchor e -padx 2 -pady 2 \
2254        7,2 $itk_component(zCutScale)   -fill y \
2255
2256    blt::table configure $inner r* c* -resize none
2257    blt::table configure $inner r7 c3 -resize expand
2258}
2259
2260
2261
2262#
2263#  camera --
2264#
2265itcl::body Rappture::VtkVolumeViewer::camera {option args} {
2266    switch -- $option {
2267        "show" {
2268            puts [array get _view]
2269        }
2270        "set" {
2271            set who [lindex $args 0]
2272            set x $_view($who)
2273            set code [catch { string is double $x } result]
2274            if { $code != 0 || !$result } {
2275                return
2276            }
2277            switch -- $who {
2278                "ortho" {
2279                    if {$_view(ortho)} {
2280                        SendCmd "camera mode ortho"
2281                    } else {
2282                        SendCmd "camera mode persp"
2283                    }
2284                }
2285                "xpan" - "ypan" {
2286                    PanCamera
2287                }
2288                "qx" - "qy" - "qz" - "qw" {
2289                    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
2290                    $_arcball quaternion $q
2291                    EventuallyRotate $q
2292                }
2293                "zoom" {
2294                    SendCmd "camera zoom $_view(zoom)"
2295                }
2296            }
2297        }
2298    }
2299}
2300
2301itcl::body Rappture::VtkVolumeViewer::ConvertToVtkData { dataobj comp } {
2302    foreach { x1 x2 xN y1 y2 yN } [$dataobj mesh $comp] break
2303    set values [$dataobj values $comp]
2304    append out "# vtk DataFile Version 2.0 \n"
2305    append out "Test data \n"
2306    append out "ASCII \n"
2307    append out "DATASET STRUCTURED_POINTS \n"
2308    append out "DIMENSIONS $xN $yN 1 \n"
2309    append out "ORIGIN 0 0 0 \n"
2310    append out "SPACING 1 1 1 \n"
2311    append out "POINT_DATA [expr $xN * $yN] \n"
2312    append out "SCALARS field float 1 \n"
2313    append out "LOOKUP_TABLE default \n"
2314    append out [join $values "\n"]
2315    append out "\n"
2316    return $out
2317}
2318
2319
2320itcl::body Rappture::VtkVolumeViewer::GetVtkData { args } {
2321    set bytes ""
2322    foreach dataobj [get] {
2323        foreach comp [$dataobj components] {
2324            set tag $dataobj-$comp
2325            #set contents [ConvertToVtkData $dataobj $comp]
2326            set contents [$dataobj blob $comp]
2327            append bytes "$contents\n\n"
2328        }
2329    }
2330    return [list .vtk $bytes]
2331}
2332
2333itcl::body Rappture::VtkVolumeViewer::GetImage { args } {
2334    if { [image width $_image(download)] > 0 &&
2335         [image height $_image(download)] > 0 } {
2336        set bytes [$_image(download) data -format "jpeg -quality 100"]
2337        set bytes [Rappture::encoding::decode -as b64 $bytes]
2338        return [list .jpg $bytes]
2339    }
2340    return ""
2341}
2342
2343itcl::body Rappture::VtkVolumeViewer::BuildDownloadPopup { popup command } {
2344    Rappture::Balloon $popup \
2345        -title "[Rappture::filexfer::label downloadWord] as..."
2346    set inner [$popup component inner]
2347    label $inner.summary -text "" -anchor w
2348    radiobutton $inner.vtk_button -text "VTK data file" \
2349        -variable [itcl::scope _downloadPopup(format)] \
2350        -font "Helvetica 9 " \
2351        -value vtk 
2352    Rappture::Tooltip::for $inner.vtk_button "Save as VTK data file."
2353    radiobutton $inner.image_button -text "Image File" \
2354        -variable [itcl::scope _downloadPopup(format)] \
2355        -value image
2356    Rappture::Tooltip::for $inner.image_button \
2357        "Save as digital image."
2358
2359    button $inner.ok -text "Save" \
2360        -highlightthickness 0 -pady 2 -padx 3 \
2361        -command $command \
2362        -compound left \
2363        -image [Rappture::icon download]
2364
2365    button $inner.cancel -text "Cancel" \
2366        -highlightthickness 0 -pady 2 -padx 3 \
2367        -command [list $popup deactivate] \
2368        -compound left \
2369        -image [Rappture::icon cancel]
2370
2371    blt::table $inner \
2372        0,0 $inner.summary -cspan 2  \
2373        1,0 $inner.vtk_button -anchor w -cspan 2 -padx { 4 0 } \
2374        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
2375        4,1 $inner.cancel -width .9i -fill y \
2376        4,0 $inner.ok -padx 2 -width .9i -fill y
2377    blt::table configure $inner r3 -height 4
2378    blt::table configure $inner r4 -pady 4
2379    raise $inner.image_button
2380    $inner.vtk_button invoke
2381    return $inner
2382}
2383
2384itcl::body Rappture::VtkVolumeViewer::SetObjectStyle { dataobj comp } {
2385    # Parse style string.
2386    set tag $dataobj-$comp
2387    set style [$dataobj style $comp]
2388    array set settings {
2389        -color \#808080
2390        -edges 0
2391        -edgecolor black
2392        -linewidth 1.0
2393        -opacity 0.4
2394        -wireframe 0
2395        -lighting 1
2396        -seeds 1
2397        -seedcolor white
2398        -visible 1
2399    }
2400    if { $dataobj != $_first } {
2401        set settings(-opacity) 1
2402    }
2403    array set settings $style
2404    SendCmd "volume add $tag"
2405    SendCmd "cutplane add $tag"
2406    SendCmd "cutplane edges 0 $tag"
2407    SendCmd "cutplane wireframe 0 $tag"
2408    SendCmd "cutplane lighting 1 $tag"
2409    SendCmd "cutplane linewidth 1 $tag"
2410    #SendCmd "cutplane linecolor 1 1 1 $tag"
2411    #SendCmd "cutplane visible $tag"
2412    foreach axis { x y z } {
2413        SendCmd "cutplane slice $axis 0.5 $tag"
2414        SendCmd "cutplane axis $axis 0 $tag"
2415    }
2416
2417    SendCmd "volume lighting $settings(-lighting) $tag"
2418    set _settings(volume-lighting) $settings(-lighting)
2419    SetColormap $dataobj $comp
2420}
2421
2422itcl::body Rappture::VtkVolumeViewer::IsValidObject { dataobj } {
2423    if {[catch {$dataobj isa Rappture::Field} valid] != 0 || !$valid} {
2424        return 0
2425    }
2426    return 1
2427}
2428
2429# ----------------------------------------------------------------------
2430# USAGE: ReceiveLegend <colormap> <title> <vmin> <vmax> <size>
2431#
2432# Invoked automatically whenever the "legend" command comes in from
2433# the rendering server.  Indicates that binary image data with the
2434# specified <size> will follow.
2435# ----------------------------------------------------------------------
2436itcl::body Rappture::VtkVolumeViewer::ReceiveLegend { colormap title vmin vmax size } {
2437    set _legendPending 0
2438    puts stderr "ReceiveLegend colormap=$colormap title=$title range=$vmin,$vmax size=$size"
2439    set _limits(vmin) $vmin
2440    set _limits(vmax) $vmax
2441    set _title $title
2442    regsub {\(mag\)} $title "" _title
2443    if { [IsConnected] } {
2444        set bytes [ReceiveBytes $size]
2445        if { ![info exists _image(legend)] } {
2446            set _image(legend) [image create photo]
2447        }
2448        $_image(legend) configure -data $bytes
2449        #puts stderr "read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
2450        if { [catch {DrawLegend $_title} errs] != 0 } {
2451            puts stderr errs=$errs
2452        }
2453    }
2454}
2455
2456#
2457# DrawLegend --
2458#
2459#       Draws the legend in it's own canvas which resides to the right
2460#       of the contour plot area.
2461#
2462itcl::body Rappture::VtkVolumeViewer::DrawLegend { name } {
2463    set c $itk_component(view)
2464    set w [winfo width $c]
2465    set h [winfo height $c]
2466    set font "Arial 8"
2467    set lineht [font metrics $font -linespace]
2468   
2469    if { [info exists _fields($name)] } {
2470        foreach { title units } $_fields($name) break
2471        if { $units != "" } {
2472            set title [format "%s (%s)" $title $units]
2473        }
2474    } else {
2475        set title $name
2476    }
2477    if { $_settings(legend-visible) } {
2478        set x [expr $w - 2]
2479        if { [$c find withtag "legend"] == "" } {
2480            set y 2
2481            $c create text $x $y \
2482                -anchor ne \
2483                -fill $itk_option(-plotforeground) -tags "title legend" \
2484                -font $font
2485            incr y $lineht
2486            $c create text $x $y \
2487                -anchor ne \
2488                -fill $itk_option(-plotforeground) -tags "vmax legend" \
2489                -font $font
2490            incr y $lineht
2491            $c create image $x $y \
2492                -anchor ne \
2493                -image $_image(legend) -tags "colormap legend"
2494            $c create text $x [expr {$h-2}] \
2495                -anchor se \
2496                -fill $itk_option(-plotforeground) -tags "vmin legend" \
2497                -font $font
2498            #$c bind colormap <Enter> [itcl::code $this EnterLegend %x %y]
2499            $c bind colormap <Leave> [itcl::code $this LeaveLegend]
2500            $c bind colormap <Motion> [itcl::code $this MotionLegend %x %y]
2501        }
2502        $c bind title <ButtonPress> [itcl::code $this Combo post]
2503        $c bind title <Enter> [itcl::code $this Combo activate]
2504        $c bind title <Leave> [itcl::code $this Combo deactivate]
2505        # Reset the item coordinates according the current size of the plot.
2506        $c itemconfigure title -text $title
2507        if { $_limits(vmin) != "" } {
2508            $c itemconfigure vmin -text [format %g $_limits(vmin)]
2509        }
2510        if { $_limits(vmax) != "" } {
2511            $c itemconfigure vmax -text [format %g $_limits(vmax)]
2512        }
2513        set y 2
2514        $c coords title $x $y
2515        incr y $lineht
2516        $c coords vmax $x $y
2517        incr y $lineht
2518        $c coords colormap $x $y
2519        $c coords vmin $x [expr {$h - 2}]
2520    }
2521}
2522
2523#
2524# EnterLegend --
2525#
2526itcl::body Rappture::VtkVolumeViewer::EnterLegend { x y } {
2527    SetLegendTip $x $y
2528}
2529
2530#
2531# MotionLegend --
2532#
2533itcl::body Rappture::VtkVolumeViewer::MotionLegend { x y } {
2534    Rappture::Tooltip::tooltip cancel
2535    set c $itk_component(view)
2536    SetLegendTip $x $y
2537}
2538
2539#
2540# LeaveLegend --
2541#
2542itcl::body Rappture::VtkVolumeViewer::LeaveLegend { } {
2543    Rappture::Tooltip::tooltip cancel
2544    .rappturetooltip configure -icon ""
2545}
2546
2547#
2548# SetLegendTip --
2549#
2550itcl::body Rappture::VtkVolumeViewer::SetLegendTip { x y } {
2551    set c $itk_component(view)
2552    set w [winfo width $c]
2553    set h [winfo height $c]
2554    set font "Arial 8"
2555    set lineht [font metrics $font -linespace]
2556   
2557    set imgHeight [image height $_image(legend)]
2558    set coords [$c coords colormap]
2559    set imgX [expr $w - [image width $_image(legend)] - 2]
2560    set imgY [expr $y - 2 * ($lineht + 2)]
2561
2562    if { [info exists _fields($_title)] } {
2563        foreach { title units } $_fields($_title) break
2564        if { $units != "" } {
2565            set title [format "%s (%s)" $title $units]
2566        }
2567    } else {
2568        set title $_title
2569    }
2570    # Make a swatch of the selected color
2571    if { [catch { $_image(legend) get 10 $imgY } pixel] != 0 } {
2572        #puts stderr "out of range: $imgY"
2573        return
2574    }
2575    if { ![info exists _image(swatch)] } {
2576        set _image(swatch) [image create photo -width 24 -height 24]
2577    }
2578    set color [eval format "\#%02x%02x%02x" $pixel]
2579    $_image(swatch) put black  -to 0 0 23 23
2580    $_image(swatch) put $color -to 1 1 22 22
2581    .rappturetooltip configure -icon $_image(swatch)
2582
2583    # Compute the value of the point
2584    if { [info exists _limits(vmax)] && [info exists _limits(vmin)] } {
2585        set t [expr 1.0 - (double($imgY) / double($imgHeight-1))]
2586        set value [expr $t * ($_limits(vmax) - $_limits(vmin)) + $_limits(vmin)]
2587    } else {
2588        set value 0.0
2589    }
2590    set tipx [expr $x + 15]
2591    set tipy [expr $y - 5]
2592    Rappture::Tooltip::text $c "$title $value"
2593    Rappture::Tooltip::tooltip show $c +$tipx,+$tipy   
2594}
2595
2596
2597# ----------------------------------------------------------------------
2598# USAGE: Slice move x|y|z <newval>
2599#
2600# Called automatically when the user drags the slider to move the
2601# cut plane that slices 3D data.  Gets the current value from the
2602# slider and moves the cut plane to the appropriate point in the
2603# data set.
2604# ----------------------------------------------------------------------
2605itcl::body Rappture::VtkVolumeViewer::Slice {option args} {
2606    switch -- $option {
2607        "move" {
2608            set axis [lindex $args 0]
2609            set oldval $_settings(axis-${axis}position)
2610            set newval [lindex $args 1]
2611            if {[llength $args] != 2} {
2612                error "wrong # args: should be \"Slice move x|y|z newval\""
2613            }
2614            set newpos [expr {0.01*$newval}]
2615            SendCmd "cutplane slice $axis $newpos"
2616        }
2617        "tooltip" {
2618            set axis [lindex $args 0]
2619            set val [$itk_component(${axis}CutScale) get]
2620            return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
2621        }
2622        default {
2623            error "bad option \"$option\": should be axis, move, or tooltip"
2624        }
2625    }
2626}
2627
2628
2629# ----------------------------------------------------------------------
2630# USAGE: _dropdown post
2631# USAGE: _dropdown unpost
2632# USAGE: _dropdown select
2633#
2634# Used internally to handle the dropdown list for this combobox.  The
2635# post/unpost options are invoked when the list is posted or unposted
2636# to manage the relief of the controlling button.  The select option
2637# is invoked whenever there is a selection from the list, to assign
2638# the value back to the gauge.
2639# ----------------------------------------------------------------------
2640itcl::body Rappture::VtkVolumeViewer::Combo {option} {
2641    set c $itk_component(view)
2642    switch -- $option {
2643        post {
2644            foreach { x1 y1 x2 y2 } [$c bbox title] break
2645            set x1 [expr [winfo width $itk_component(view)] - [winfo reqwidth $itk_component(fieldmenu)]]
2646            set x [expr $x1 + [winfo rootx $itk_component(view)]]
2647            set y [expr $y2 + [winfo rooty $itk_component(view)]]
2648            puts stderr "combo x=$x y=$y"
2649            tk_popup $itk_component(fieldmenu) $x $y
2650        }
2651        activate {
2652            $c itemconfigure title -fill red
2653        }
2654        deactivate {
2655            $c itemconfigure title -fill white
2656        }
2657        invoke {
2658            $itk_component(field) value $_currentField
2659            AdjustSetting volume-field
2660        }
2661        default {
2662            error "bad option \"$option\": should be post, unpost, select"
2663        }
2664    }
2665}
2666
Note: See TracBrowser for help on using the repository browser.