source: branches/1.4/gui/scripts/vtkvolumeviewer.tcl @ 5732

Last change on this file since 5732 was 5732, checked in by ldelgass, 9 years ago

merge 5729 from trunk

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