source: trunk/gui/scripts/vtkvolumeviewer.tcl @ 3093

Last change on this file since 3093 was 2792, checked in by gah, 13 years ago

move "package require vtk" into constructor so that applications do not have to load vtk to use Rappture widgets

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