source: branches/1.5/gui/scripts/vtkvolumeviewer.tcl @ 6127

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

Merge r6052:6053,r6066:6069,r6080 from trunk

File size: 82.6 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 { args } {
42        Rappture::VisViewer::constructor
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 {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        foreach axis { x y z } {
1030            set label [$_first hints ${axis}label]
1031            if { $label != "" } {
1032                SendCmd [list axis name $axis $label]
1033            }
1034            set units [$_first hints ${axis}units]
1035            if { $units != "" } {
1036                SendCmd [list axis units $axis $units]
1037            }
1038        }
1039        $itk_component(field) choices delete 0 end
1040        $itk_component(fieldmenu) delete 0 end
1041        array unset _fields
1042        set _curFldName ""
1043        foreach cname [$_first components] {
1044            foreach fname [$_first fieldnames $cname] {
1045                if { [info exists _fields($fname)] } {
1046                    continue
1047                }
1048                foreach { label units components } \
1049                    [$_first fieldinfo $fname] break
1050                # Only scalar fields are valid
1051                if {$_allowMultiComponent || $components == 1} {
1052                    $itk_component(field) choices insert end "$fname" "$label"
1053                    $itk_component(fieldmenu) add radiobutton -label "$label" \
1054                        -value $label -variable [itcl::scope _curFldLabel] \
1055                        -selectcolor red \
1056                        -activebackground $itk_option(-plotbackground) \
1057                        -activeforeground $itk_option(-plotforeground) \
1058                        -font "Arial 8" \
1059                        -command [itcl::code $this Combo invoke]
1060                    set _fields($fname) [list $label $units $components]
1061                    if { $_curFldName == "" } {
1062                        set _curFldName $fname
1063                        set _curFldLabel $label
1064                    }
1065                }
1066            }
1067        }
1068        $itk_component(field) value $_curFldLabel
1069    }
1070
1071    InitSettings -color \
1072        -volumevisible \
1073        -volumematerial \
1074        -volumelighting -volumeopacity -volumequality -volumeoutline \
1075        -cutplanesvisible \
1076        -xcutplaneposition -ycutplaneposition -zcutplaneposition \
1077        -xcutplanevisible -ycutplanevisible -zcutplanevisible
1078
1079    if { $_reset } {
1080        SendCmd "camera reset"
1081        SendCmd "camera zoom $_view(-zoom)"
1082        RequestLegend
1083        set _reset 0
1084    }
1085    # Actually write the commands to the server socket.  If it fails, we don't
1086    # care.  We're finished here.
1087    blt::busy hold $itk_component(hull)
1088    StopBufferingCommands
1089    blt::busy release $itk_component(hull)
1090}
1091
1092# ----------------------------------------------------------------------
1093# USAGE: CurrentDatasets ?-all -visible? ?dataobjs?
1094#
1095# Returns a list of server IDs for the current datasets being displayed.  This
1096# is normally a single ID, but it might be a list of IDs if the current data
1097# object has multiple components.
1098# ----------------------------------------------------------------------
1099itcl::body Rappture::VtkVolumeViewer::CurrentDatasets {args} {
1100    set flag [lindex $args 0]
1101    switch -- $flag {
1102        "-all" {
1103            if { [llength $args] > 1 } {
1104                error "CurrentDatasets: can't specify dataobj after \"-all\""
1105            }
1106            set dlist [get -objects]
1107        }
1108        "-visible" {
1109            if { [llength $args] > 1 } {
1110                set dlist {}
1111                set args [lrange $args 1 end]
1112                foreach dataobj $args {
1113                    if { [info exists _obj2ovride($dataobj-raise)] } {
1114                        lappend dlist $dataobj
1115                    }
1116                }
1117            } else {
1118                set dlist [get -visible]
1119            }
1120        }
1121        default {
1122            set dlist $args
1123        }
1124    }
1125    set rlist ""
1126    foreach dataobj $dlist {
1127        foreach comp [$dataobj components] {
1128            set tag $dataobj-$comp
1129            if { [info exists _datasets($tag)] && $_datasets($tag) } {
1130                lappend rlist $tag
1131            }
1132        }
1133    }
1134    return $rlist
1135}
1136
1137# ----------------------------------------------------------------------
1138# USAGE: Zoom in
1139# USAGE: Zoom out
1140# USAGE: Zoom reset
1141#
1142# Called automatically when the user clicks on one of the zoom
1143# controls for this widget.  Changes the zoom for the current view.
1144# ----------------------------------------------------------------------
1145itcl::body Rappture::VtkVolumeViewer::Zoom {option} {
1146    switch -- $option {
1147        "in" {
1148            set _view(-zoom) [expr {$_view(-zoom)*1.25}]
1149            SendCmd "camera zoom $_view(-zoom)"
1150        }
1151        "out" {
1152            set _view(-zoom) [expr {$_view(-zoom)*0.8}]
1153            SendCmd "camera zoom $_view(-zoom)"
1154        }
1155        "reset" {
1156            array set _view {
1157                -qw      0.853553
1158                -qx      -0.353553
1159                -qy      0.353553
1160                -qz      0.146447
1161                -xpan    0
1162                -ypan    0
1163                -zoom    1.0
1164            }
1165            if { $_first != "" } {
1166                set location [$_first hints camera]
1167                if { $location != "" } {
1168                    array set _view $location
1169                }
1170            }
1171            $_arcball quaternion [ViewToQuaternion]
1172            DoRotate
1173            SendCmd "camera reset"
1174        }
1175    }
1176}
1177
1178itcl::body Rappture::VtkVolumeViewer::PanCamera {} {
1179    set x $_view(-xpan)
1180    set y $_view(-ypan)
1181    SendCmd "camera pan $x $y"
1182}
1183
1184# ----------------------------------------------------------------------
1185# USAGE: Rotate click <x> <y>
1186# USAGE: Rotate drag <x> <y>
1187# USAGE: Rotate release <x> <y>
1188#
1189# Called automatically when the user clicks/drags/releases in the
1190# plot area.  Moves the plot according to the user's actions.
1191# ----------------------------------------------------------------------
1192itcl::body Rappture::VtkVolumeViewer::Rotate {option x y} {
1193    switch -- $option {
1194        "click" {
1195            $itk_component(view) configure -cursor fleur
1196            set _click(x) $x
1197            set _click(y) $y
1198        }
1199        "drag" {
1200            if {[array size _click] == 0} {
1201                Rotate click $x $y
1202            } else {
1203                set w [winfo width $itk_component(view)]
1204                set h [winfo height $itk_component(view)]
1205                if {$w <= 0 || $h <= 0} {
1206                    return
1207                }
1208
1209                if {[catch {
1210                    # this fails sometimes for no apparent reason
1211                    set dx [expr {double($x-$_click(x))/$w}]
1212                    set dy [expr {double($y-$_click(y))/$h}]
1213                }]} {
1214                    return
1215                }
1216                if { $dx == 0 && $dy == 0 } {
1217                    return
1218                }
1219                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1220                EventuallyRotate $q
1221                set _click(x) $x
1222                set _click(y) $y
1223            }
1224        }
1225        "release" {
1226            Rotate drag $x $y
1227            $itk_component(view) configure -cursor ""
1228            catch {unset _click}
1229        }
1230        default {
1231            error "bad option \"$option\": should be click, drag, release"
1232        }
1233    }
1234}
1235
1236itcl::body Rappture::VtkVolumeViewer::Pick {x y} {
1237    foreach tag [CurrentDatasets -visible] {
1238        SendCmd "dataset getscalar pixel $x $y $tag"
1239    }
1240}
1241
1242# ----------------------------------------------------------------------
1243# USAGE: $this Pan click x y
1244#        $this Pan drag x y
1245#        $this Pan release x y
1246#
1247# Called automatically when the user clicks on one of the zoom
1248# controls for this widget.  Changes the zoom for the current view.
1249# ----------------------------------------------------------------------
1250itcl::body Rappture::VtkVolumeViewer::Pan {option x y} {
1251    switch -- $option {
1252        "set" {
1253            set w [winfo width $itk_component(view)]
1254            set h [winfo height $itk_component(view)]
1255            set x [expr $x / double($w)]
1256            set y [expr $y / double($h)]
1257            set _view(-xpan) [expr $_view(-xpan) + $x]
1258            set _view(-ypan) [expr $_view(-ypan) + $y]
1259            PanCamera
1260            return
1261        }
1262        "click" {
1263            set _click(x) $x
1264            set _click(y) $y
1265            $itk_component(view) configure -cursor hand1
1266        }
1267        "drag" {
1268            if { ![info exists _click(x)] } {
1269                set _click(x) $x
1270            }
1271            if { ![info exists _click(y)] } {
1272                set _click(y) $y
1273            }
1274            set w [winfo width $itk_component(view)]
1275            set h [winfo height $itk_component(view)]
1276            set dx [expr ($_click(x) - $x)/double($w)]
1277            set dy [expr ($_click(y) - $y)/double($h)]
1278            set _click(x) $x
1279            set _click(y) $y
1280            set _view(-xpan) [expr $_view(-xpan) - $dx]
1281            set _view(-ypan) [expr $_view(-ypan) - $dy]
1282            PanCamera
1283        }
1284        "release" {
1285            Pan drag $x $y
1286            $itk_component(view) configure -cursor ""
1287        }
1288        default {
1289            error "unknown option \"$option\": should set, click, drag, or release"
1290        }
1291    }
1292}
1293
1294# ----------------------------------------------------------------------
1295# USAGE: InitSettings <what> ?<value>?
1296#
1297# Used internally to update rendering settings whenever parameters
1298# change in the popup settings panel.  Sends the new settings off
1299# to the back end.
1300# ----------------------------------------------------------------------
1301itcl::body Rappture::VtkVolumeViewer::InitSettings { args } {
1302    foreach spec $args {
1303        if { [info exists _settings($_first${spec})] } {
1304            # Reset global setting with dataobj specific setting
1305            set _settings($spec) $_settings($_first${spec})
1306        }
1307        AdjustSetting $spec
1308    }
1309}
1310
1311#
1312# AdjustSetting --
1313#
1314# Changes/updates a specific setting in the widget.  There are
1315# usually user-setable option.  Commands are sent to the render
1316# server.
1317#
1318itcl::body Rappture::VtkVolumeViewer::AdjustSetting {what {value ""}} {
1319    if { ![isconnected] } {
1320        return
1321    }
1322    switch -- $what {
1323        "-background" {
1324            set bgcolor [$itk_component(background) value]
1325            set _settings($what) $bgcolor
1326            array set fgcolors {
1327                "black" "white"
1328                "white" "black"
1329                "grey"  "black"
1330            }
1331            configure -plotbackground $bgcolor \
1332                -plotforeground $fgcolors($bgcolor)
1333            $itk_component(view) delete "legend"
1334            DrawLegend
1335        }
1336        "-volumeoutline" {
1337            set bool $_settings($what)
1338            SendCmd "outline visible 0"
1339            foreach tag [CurrentDatasets -visible] {
1340                SendCmd "outline visible $bool $tag"
1341            }
1342        }
1343        "-legendvisible" {
1344            DrawLegend
1345        }
1346        "-volumevisible" {
1347            set bool $_settings($what)
1348            foreach tag [CurrentDatasets -visible] {
1349                SendCmd "volume visible $bool $tag"
1350            }
1351            if { $bool } {
1352                Rappture::Tooltip::for $itk_component(volume) \
1353                    "Hide the volume"
1354            } else {
1355                Rappture::Tooltip::for $itk_component(volume) \
1356                    "Show the volume"
1357            }
1358        }
1359        "-volumematerial" {
1360            set val $_settings($what)
1361            set diffuse [expr {0.01*$val}]
1362            set specular [expr {0.01*$val}]
1363            #set power [expr {sqrt(160*$val+1.0)}]
1364            set power [expr {$val+1.0}]
1365            foreach tag [CurrentDatasets -visible] {
1366                SendCmd "volume shading diffuse $diffuse $tag"
1367                SendCmd "volume shading specular $specular $power $tag"
1368            }
1369        }
1370        "-volumelighting" {
1371            set bool $_settings($what)
1372            foreach tag [CurrentDatasets -visible] {
1373                SendCmd "volume lighting $bool $tag"
1374            }
1375        }
1376        "-volumeopacity" {
1377            set val $_settings($what)
1378            set val [expr {0.01*$val}]
1379            foreach tag [CurrentDatasets -visible] {
1380                SendCmd "volume opacity $val $tag"
1381            }
1382        }
1383        "-volumequality" {
1384            set val $_settings($what)
1385            set val [expr {0.01*$val}]
1386            foreach tag [CurrentDatasets -visible] {
1387                SendCmd "volume quality $val $tag"
1388            }
1389        }
1390        "-axesvisible" {
1391            set bool $_settings($what)
1392            SendCmd "axis visible all $bool"
1393        }
1394        "-axislabels" {
1395            set bool $_settings($what)
1396            SendCmd "axis labels all $bool"
1397        }
1398        "-axisminorticks" {
1399            set bool $_settings($what)
1400            SendCmd "axis minticks all $bool"
1401        }
1402        "-xgrid" - "-ygrid" - "-zgrid" {
1403            set axis [string range $what 1 1]
1404            set bool $_settings($what)
1405            SendCmd "axis grid $axis $bool"
1406        }
1407        "-axisflymode" {
1408            set mode [$itk_component(axismode) value]
1409            set mode [$itk_component(axismode) translate $mode]
1410            set _settings($what) $mode
1411            SendCmd "axis flymode $mode"
1412        }
1413        "-cutplanesvisible" {
1414            set bool $_settings($what)
1415            foreach dataset [CurrentDatasets -visible] {
1416                SendCmd "$_cutplaneCmd visible $bool $dataset"
1417            }
1418        }
1419        "-cutplanelighting" {
1420            set bool $_settings($what)
1421            foreach dataset [CurrentDatasets -visible] {
1422                if {$_cutplaneCmd != "imgcutplane"} {
1423                    SendCmd "$_cutplaneCmd lighting $bool $dataset"
1424                } else {
1425                    if {$bool} {
1426                        set ambient 0.0
1427                        set diffuse 1.0
1428                    } else {
1429                        set ambient 1.0
1430                        set diffuse 0.0
1431                    }
1432                    SendCmd "imgcutplane material $ambient $diffuse $dataset"
1433                }
1434            }
1435        }
1436        "-cutplaneopacity" {
1437            set val $_settings($what)
1438            set sval [expr { 0.01 * double($val) }]
1439            foreach dataset [CurrentDatasets -visible] {
1440                SendCmd "$_cutplaneCmd opacity $sval $dataset"
1441            }
1442        }
1443        "-xcutplanevisible" - "-ycutplanevisible" - "-zcutplanevisible" {
1444            set axis [string range $what 1 1]
1445            set bool $_settings($what)
1446            if { $bool } {
1447                $itk_component(${axis}CutScale) configure -state normal \
1448                    -troughcolor white
1449            } else {
1450                $itk_component(${axis}CutScale) configure -state disabled \
1451                    -troughcolor grey82
1452            }
1453            foreach dataset [CurrentDatasets -visible] {
1454                SendCmd "$_cutplaneCmd axis $axis $bool $dataset"
1455            }
1456        }
1457        "-xcutplaneposition" - "-ycutplaneposition" - "-zcutplaneposition" {
1458            set axis [string range $what 1 1]
1459            set pos [expr $_settings($what) * 0.01]
1460            foreach dataset [CurrentDatasets -visible] {
1461                SendCmd "$_cutplaneCmd slice ${axis} ${pos} $dataset"
1462            }
1463            set _cutplanePending 0
1464        }
1465        "-color" {
1466            set color [$itk_component(colormap) value]
1467            set _settings($what) $color
1468            foreach dataset [CurrentDatasets -visible $_first] {
1469                foreach {dataobj comp} [split $dataset -] break
1470                ChangeColormap $dataobj $comp $color
1471            }
1472            EventuallyRequestLegend
1473        }
1474        "-field" {
1475            set label [$itk_component(field) value]
1476            set fname [$itk_component(field) translate $label]
1477            set _settings($what) $fname
1478            if { [info exists _fields($fname)] } {
1479                foreach { label units components } $_fields($fname) break
1480                if { !$_allowMultiComponent && $components > 1 } {
1481                    puts stderr "Can't use a vector field in a volume"
1482                    return
1483                } else {
1484                    if { $components > 1 } {
1485                        set _colorMode vmag
1486                    } else {
1487                        set _colorMode scalar
1488                    }
1489                }
1490                set _curFldName $fname
1491                set _curFldLabel $label
1492            } else {
1493                puts stderr "unknown field \"$fname\""
1494                return
1495            }
1496            foreach dataset [CurrentDatasets -visible $_first] {
1497                #SendCmd "$_cutplaneCmd colormode $_colorMode $_curFldName $dataset"
1498                SendCmd "dataset scalar $_curFldName $dataset"
1499            }
1500            SendCmd "camera reset"
1501            DrawLegend
1502        }
1503        default {
1504            error "don't know how to fix $what"
1505        }
1506    }
1507}
1508
1509#
1510# RequestLegend --
1511#
1512# Request a new legend from the server.  The size of the legend
1513# is determined from the height of the canvas.
1514#
1515itcl::body Rappture::VtkVolumeViewer::RequestLegend {} {
1516    set _legendPending 0
1517    set font "Arial 8"
1518    set lineht [font metrics $font -linespace]
1519    set w 12
1520    set h [expr {$_height - 3 * ($lineht + 2)}]
1521    if { $h < 1 } {
1522        return
1523    }
1524    # Set the legend on the first volume dataset.
1525    foreach dataset [CurrentDatasets -visible $_first] {
1526        foreach {dataobj comp} [split $dataset -] break
1527        if { [info exists _dataset2style($dataset)] } {
1528            #SendCmd "legend $_dataset2style($dataset) $_colorMode $_curFldName {} $w $h 0"
1529            SendCmd "legend2 $_dataset2style($dataset) $w $h"
1530            break;
1531        }
1532    }
1533}
1534
1535#
1536# ChangeColormap --
1537#
1538itcl::body Rappture::VtkVolumeViewer::ChangeColormap {dataobj comp color} {
1539    set tag $dataobj-$comp
1540    if { ![info exist _style($tag)] } {
1541        error "no initial colormap"
1542    }
1543    array set style $_style($tag)
1544    set style(-color) $color
1545    set _style($tag) [array get style]
1546    SetColormap $dataobj $comp
1547}
1548
1549#
1550# SetColormap --
1551#
1552itcl::body Rappture::VtkVolumeViewer::SetColormap { dataobj comp } {
1553    array set style {
1554        -color BCGYR
1555        -levels 6
1556    }
1557    set tag $dataobj-$comp
1558    if { ![info exists _initialStyle($tag)] } {
1559        # Save the initial component style.
1560        set _initialStyle($tag) [$dataobj style $comp]
1561    }
1562
1563    # Override defaults with initial style defined in xml.
1564    array set style $_initialStyle($tag)
1565
1566    if { ![info exists _style($tag)] } {
1567        set _style($tag) [array get style]
1568    }
1569    # Override initial style with current style.
1570    array set style $_style($tag)
1571
1572    set name "$style(-color):$style(-levels)"
1573    if { ![info exists _colormaps($name)] } {
1574        BuildColormap $name [array get style]
1575        set _colormaps($name) 1
1576    }
1577    if { ![info exists _dataset2style($tag)] ||
1578         $_dataset2style($tag) != $name } {
1579        SendCmd "volume colormap $name $tag"
1580        SendCmd "$_cutplaneCmd colormap $name-opaque $tag"
1581        set _dataset2style($tag) $name
1582    }
1583}
1584
1585#
1586# BuildColormap --
1587#
1588itcl::body Rappture::VtkVolumeViewer::BuildColormap { name styles } {
1589    array set style $styles
1590    set cmap [ColorsToColormap $style(-color)]
1591    if { [llength $cmap] == 0 } {
1592        set cmap "0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0"
1593    }
1594    set max 1.0
1595
1596    set opaqueAmap "0.0 1.0 1.0 1.0"
1597    #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"
1598    # Approximate cubic opacity curve
1599    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"
1600    SendCmd "colormap add $name { $cmap } { $amap }"
1601    SendCmd "colormap add $name-opaque { $cmap } { $opaqueAmap }"
1602}
1603
1604# ----------------------------------------------------------------------
1605# CONFIGURATION OPTION: -plotbackground
1606# ----------------------------------------------------------------------
1607itcl::configbody Rappture::VtkVolumeViewer::plotbackground {
1608    if { [isconnected] } {
1609        set color $itk_option(-plotbackground)
1610        set rgb [Color2RGB $color]
1611        SendCmd "screen bgcolor $rgb"
1612        $itk_component(legend) configure -background $color
1613    }
1614}
1615
1616# ----------------------------------------------------------------------
1617# CONFIGURATION OPTION: -plotforeground
1618# ----------------------------------------------------------------------
1619itcl::configbody Rappture::VtkVolumeViewer::plotforeground {
1620    if { [isconnected] } {
1621        set color $itk_option(-plotforeground)
1622        set rgb [Color2RGB $color]
1623        SendCmd "axis color all $rgb"
1624        SendCmd "outline color $rgb"
1625        SendCmd "$_cutplaneCmd color $rgb"
1626        $itk_component(legend) itemconfigure labels -fill $color
1627        $itk_component(legend) itemconfigure limits -fill $color
1628    }
1629}
1630
1631itcl::body Rappture::VtkVolumeViewer::BuildViewTab {} {
1632    set font [option get $itk_component(hull) font Font]
1633
1634    set inner [$itk_component(main) insert end \
1635        -title "View Settings" \
1636        -icon [Rappture::icon wrench]]
1637    $inner configure -borderwidth 4
1638
1639    checkbutton $inner.axes \
1640        -text "Axes" \
1641        -variable [itcl::scope _settings(-axesvisible)] \
1642        -command [itcl::code $this AdjustSetting -axesvisible] \
1643        -font $font
1644
1645    checkbutton $inner.outline \
1646        -text "Outline" \
1647        -variable [itcl::scope _settings(-volumeoutline)] \
1648        -command [itcl::code $this AdjustSetting -volumeoutline] \
1649        -font $font
1650
1651    checkbutton $inner.legend \
1652        -text "Legend" \
1653        -variable [itcl::scope _settings(-legendvisible)] \
1654        -command [itcl::code $this AdjustSetting -legendvisible] \
1655        -font $font
1656
1657    checkbutton $inner.volume \
1658        -text "Volume" \
1659        -variable [itcl::scope _settings(-volumevisible)] \
1660        -command [itcl::code $this AdjustSetting -volumevisible] \
1661        -font $font
1662
1663    label $inner.background_l -text "Background" -font $font
1664    itk_component add background {
1665        Rappture::Combobox $inner.background -width 10 -editable no
1666    }
1667    $inner.background choices insert end \
1668        "black" "black" \
1669        "white" "white" \
1670        "grey"  "grey"
1671
1672    $itk_component(background) value $_settings(-background)
1673    bind $inner.background <<Value>> \
1674        [itcl::code $this AdjustSetting -background]
1675
1676    blt::table $inner \
1677        0,0 $inner.axes -cspan 2 -anchor w \
1678        1,0 $inner.outline -cspan 2 -anchor w \
1679        2,0 $inner.volume -cspan 2 -anchor w \
1680        3,0 $inner.legend -cspan 2 -anchor w \
1681        4,0 $inner.background_l -anchor e -pady 2 \
1682        4,1 $inner.background -fill x
1683
1684    blt::table configure $inner r* -resize none
1685    blt::table configure $inner r5 -resize expand
1686}
1687
1688itcl::body Rappture::VtkVolumeViewer::BuildVolumeTab {} {
1689    set font [option get $itk_component(hull) font Font]
1690    #set bfont [option get $itk_component(hull) boldFont Font]
1691    set bfont "Arial 9 bold"
1692
1693    set inner [$itk_component(main) insert end \
1694        -title "Volume Settings" \
1695        -icon [Rappture::icon volume-on]]
1696    $inner configure -borderwidth 4
1697
1698    checkbutton $inner.visibility \
1699        -text "Visible" \
1700        -font $font \
1701        -variable [itcl::scope _settings(-volumevisible)] \
1702        -command [itcl::code $this AdjustSetting -volumevisible]
1703
1704    checkbutton $inner.lighting \
1705        -text "Enable Lighting" \
1706        -font $font \
1707        -variable [itcl::scope _settings(-volumelighting)] \
1708        -command [itcl::code $this AdjustSetting -volumelighting]
1709
1710    label $inner.dim_l -text "Dim" -font $font
1711    ::scale $inner.material -from 0 -to 100 -orient horizontal \
1712        -variable [itcl::scope _settings(-volumematerial)] \
1713        -showvalue off \
1714        -command [itcl::code $this AdjustSetting -volumematerial]
1715    label $inner.bright_l -text "Bright" -font $font
1716
1717    label $inner.opacity_l -text "Opacity" -font $font
1718    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1719        -variable [itcl::scope _settings(-volumeopacity)] \
1720        -showvalue off \
1721        -command [itcl::code $this AdjustSetting -volumeopacity]
1722
1723    label $inner.quality_l -text "Quality" -font $font
1724    ::scale $inner.quality -from 0 -to 100 -orient horizontal \
1725        -variable [itcl::scope _settings(-volumequality)] \
1726        -showvalue off \
1727        -command [itcl::code $this AdjustSetting -volumequality]
1728
1729    label $inner.field_l -text "Field" -font $font
1730    itk_component add field {
1731        Rappture::Combobox $inner.field -editable no
1732    }
1733    bind $inner.field <<Value>> \
1734        [itcl::code $this AdjustSetting -field]
1735
1736    label $inner.colormap_l -text "Colormap" -font $font
1737    itk_component add colormap {
1738        Rappture::Combobox $inner.colormap -editable no
1739    }
1740    $inner.colormap choices insert end [GetColormapList]
1741    bind $inner.colormap <<Value>> \
1742        [itcl::code $this AdjustSetting -color]
1743    $itk_component(colormap) value $_settings(-color)
1744
1745    blt::table $inner \
1746        0,0 $inner.field_l -anchor w -pady 2 \
1747        0,1 $inner.field -fill x -pady 2 -cspan 3 \
1748        1,0 $inner.visibility -anchor w -pady 2 -cspan 4 \
1749        2,0 $inner.lighting -anchor w -pady 2 -cspan 4 \
1750        3,0 $inner.dim_l -anchor e -pady 2 \
1751        3,1 $inner.material -fill x -pady 2 -cspan 2 \
1752        3,3 $inner.bright_l -anchor w -pady 2 \
1753        4,0 $inner.opacity_l -anchor w -pady 2 -cspan 4 \
1754        5,0 $inner.opacity -fill x -pady 2 -cspan 4 \
1755        6,0 $inner.quality_l -anchor w -pady 2 -cspan 4 \
1756        7,0 $inner.quality -fill x -pady 2 -cspan 4 \
1757        8,0 $inner.colormap_l -anchor w -pady 2 \
1758        8,1 $inner.colormap -fill x -pady 2 -cspan 3
1759
1760    blt::table configure $inner r* c0 c1 c3 -resize none
1761    blt::table configure $inner r9 c2 -resize expand
1762}
1763
1764itcl::body Rappture::VtkVolumeViewer::BuildAxisTab {} {
1765    set font [option get $itk_component(hull) font Font]
1766
1767    set inner [$itk_component(main) insert end \
1768        -title "Axis Settings" \
1769        -icon [Rappture::icon axis2]]
1770    $inner configure -borderwidth 4
1771
1772    checkbutton $inner.visible \
1773        -text "Axes" \
1774        -variable [itcl::scope _settings(-axesvisible)] \
1775        -command [itcl::code $this AdjustSetting -axesvisible] \
1776        -font $font
1777
1778    checkbutton $inner.labels \
1779        -text "Axis Labels" \
1780        -variable [itcl::scope _settings(-axislabels)] \
1781        -command [itcl::code $this AdjustSetting -axislabels] \
1782        -font $font
1783    label $inner.grid_l -text "Grid" -font $font
1784    checkbutton $inner.xgrid \
1785        -text "X" \
1786        -variable [itcl::scope _settings(-xgrid)] \
1787        -command [itcl::code $this AdjustSetting -xgrid] \
1788        -font $font
1789    checkbutton $inner.ygrid \
1790        -text "Y" \
1791        -variable [itcl::scope _settings(-ygrid)] \
1792        -command [itcl::code $this AdjustSetting -ygrid] \
1793        -font $font
1794    checkbutton $inner.zgrid \
1795        -text "Z" \
1796        -variable [itcl::scope _settings(-zgrid)] \
1797        -command [itcl::code $this AdjustSetting -zgrid] \
1798        -font $font
1799    checkbutton $inner.minorticks \
1800        -text "Minor Ticks" \
1801        -variable [itcl::scope _settings(-axisminorticks)] \
1802        -command [itcl::code $this AdjustSetting -axisminorticks] \
1803        -font $font
1804
1805    label $inner.mode_l -text "Mode" -font $font
1806
1807    itk_component add axismode {
1808        Rappture::Combobox $inner.mode -width 10 -editable no
1809    }
1810    $inner.mode choices insert end \
1811        "static_triad"    "static" \
1812        "closest_triad"   "closest" \
1813        "furthest_triad"  "farthest" \
1814        "outer_edges"     "outer"
1815    $itk_component(axismode) value $_settings(-axisflymode)
1816    bind $inner.mode <<Value>> [itcl::code $this AdjustSetting -axisflymode]
1817
1818    blt::table $inner \
1819        0,0 $inner.visible -anchor w -cspan 4 \
1820        1,0 $inner.labels -anchor w -cspan 4 \
1821        2,0 $inner.minorticks -anchor w -cspan 4 \
1822        4,0 $inner.grid_l -anchor w \
1823        4,1 $inner.xgrid -anchor w \
1824        4,2 $inner.ygrid -anchor w \
1825        4,3 $inner.zgrid -anchor w \
1826        5,0 $inner.mode_l -anchor w -padx { 2 0 } \
1827        5,1 $inner.mode -fill x -cspan 3
1828
1829    blt::table configure $inner r* c* -resize none
1830    blt::table configure $inner r7 c6 -resize expand
1831    blt::table configure $inner r3 -height 0.125i
1832}
1833
1834itcl::body Rappture::VtkVolumeViewer::BuildCameraTab {} {
1835    set font [option get $itk_component(hull) font Font]
1836    set inner [$itk_component(main) insert end \
1837        -title "Camera Settings" \
1838        -icon [Rappture::icon camera]]
1839    $inner configure -borderwidth 4
1840
1841    label $inner.view_l -text "view" -font $font
1842    set f [frame $inner.view]
1843    foreach side { front back left right top bottom } {
1844        button $f.$side  -image [Rappture::icon view$side] \
1845            -command [itcl::code $this SetOrientation $side]
1846        Rappture::Tooltip::for $f.$side "Change the view to $side"
1847        pack $f.$side -side left
1848    }
1849    blt::table $inner \
1850        0,0 $inner.view_l -anchor e -pady 2 \
1851        0,1 $inner.view -anchor w -pady 2
1852    blt::table configure $inner r0 -resize none
1853
1854    set row 1
1855    set labels { qx qy qz qw xpan ypan zoom }
1856    foreach tag $labels {
1857        label $inner.${tag}label -text $tag -font $font
1858        entry $inner.${tag} -font $font  -bg white \
1859            -textvariable [itcl::scope _view(-$tag)]
1860        bind $inner.${tag} <Return> \
1861            [itcl::code $this camera set -${tag}]
1862        bind $inner.${tag} <KP_Enter> \
1863            [itcl::code $this camera set -${tag}]
1864        blt::table $inner \
1865            $row,0 $inner.${tag}label -anchor e -pady 2 \
1866            $row,1 $inner.${tag} -anchor w -pady 2
1867        blt::table configure $inner r$row -resize none
1868        incr row
1869    }
1870    checkbutton $inner.ortho \
1871        -text "Orthographic Projection" \
1872        -variable [itcl::scope _view(-ortho)] \
1873        -command [itcl::code $this camera set -ortho] \
1874        -font $font
1875    blt::table $inner \
1876            $row,0 $inner.ortho -cspan 2 -anchor w -pady 2
1877    blt::table configure $inner r$row -resize none
1878    incr row
1879
1880    blt::table configure $inner c0 c1 -resize none
1881    blt::table configure $inner c2 -resize expand
1882    blt::table configure $inner r$row -resize expand
1883}
1884
1885itcl::body Rappture::VtkVolumeViewer::BuildCutplaneTab {} {
1886    set font [option get $itk_component(hull) font Font]
1887
1888    set inner [$itk_component(main) insert end \
1889        -title "Cutplane Settings" \
1890        -icon [Rappture::icon cutbutton]]
1891
1892    $inner configure -borderwidth 4
1893
1894    checkbutton $inner.visible \
1895        -text "Show Cutplanes" \
1896        -variable [itcl::scope _settings(-cutplanesvisible)] \
1897        -command [itcl::code $this AdjustSetting -cutplanesvisible] \
1898        -font $font
1899
1900    checkbutton $inner.lighting \
1901        -text "Enable Lighting" \
1902        -variable [itcl::scope _settings(-cutplanelighting)] \
1903        -command [itcl::code $this AdjustSetting -cutplanelighting] \
1904        -font $font
1905
1906    label $inner.opacity_l -text "Opacity" -font $font
1907    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1908        -variable [itcl::scope _settings(-cutplaneopacity)] \
1909        -width 10 \
1910        -showvalue off \
1911        -command [itcl::code $this AdjustSetting -cutplaneopacity]
1912    $inner.opacity set $_settings(-cutplaneopacity)
1913
1914    # X-value slicer...
1915    itk_component add xCutButton {
1916        Rappture::PushButton $inner.xbutton \
1917            -onimage [Rappture::icon x-cutplane] \
1918            -offimage [Rappture::icon x-cutplane] \
1919            -command [itcl::code $this AdjustSetting -xcutplanevisible] \
1920            -variable [itcl::scope _settings(-xcutplanevisible)]
1921    }
1922    Rappture::Tooltip::for $itk_component(xCutButton) \
1923        "Toggle the X-axis cutplane on/off"
1924    $itk_component(xCutButton) select
1925
1926    itk_component add xCutScale {
1927        ::scale $inner.xval -from 100 -to 0 \
1928            -width 10 -orient vertical -showvalue yes \
1929            -borderwidth 1 -highlightthickness 0 \
1930            -command [itcl::code $this EventuallySetCutplane x] \
1931            -variable [itcl::scope _settings(-xcutplaneposition)]
1932    } {
1933        usual
1934        ignore -borderwidth -highlightthickness
1935    }
1936    # Set the default cutplane value before disabling the scale.
1937    $itk_component(xCutScale) set 50
1938    $itk_component(xCutScale) configure -state disabled
1939    Rappture::Tooltip::for $itk_component(xCutScale) \
1940        "@[itcl::code $this Slice tooltip x]"
1941
1942    # Y-value slicer...
1943    itk_component add yCutButton {
1944        Rappture::PushButton $inner.ybutton \
1945            -onimage [Rappture::icon y-cutplane] \
1946            -offimage [Rappture::icon y-cutplane] \
1947            -command [itcl::code $this AdjustSetting -ycutplanevisible] \
1948            -variable [itcl::scope _settings(-ycutplanevisible)]
1949    }
1950    Rappture::Tooltip::for $itk_component(yCutButton) \
1951        "Toggle the Y-axis cutplane on/off"
1952    $itk_component(yCutButton) select
1953
1954    itk_component add yCutScale {
1955        ::scale $inner.yval -from 100 -to 0 \
1956            -width 10 -orient vertical -showvalue yes \
1957            -borderwidth 1 -highlightthickness 0 \
1958            -command [itcl::code $this EventuallySetCutplane y] \
1959            -variable [itcl::scope _settings(-ycutplaneposition)]
1960    } {
1961        usual
1962        ignore -borderwidth -highlightthickness
1963    }
1964    Rappture::Tooltip::for $itk_component(yCutScale) \
1965        "@[itcl::code $this Slice tooltip y]"
1966    # Set the default cutplane value before disabling the scale.
1967    $itk_component(yCutScale) set 50
1968    $itk_component(yCutScale) configure -state disabled
1969
1970    # Z-value slicer...
1971    itk_component add zCutButton {
1972        Rappture::PushButton $inner.zbutton \
1973            -onimage [Rappture::icon z-cutplane] \
1974            -offimage [Rappture::icon z-cutplane] \
1975            -command [itcl::code $this AdjustSetting -zcutplanevisible] \
1976            -variable [itcl::scope _settings(-zcutplanevisible)]
1977    }
1978    Rappture::Tooltip::for $itk_component(zCutButton) \
1979        "Toggle the Z-axis cutplane on/off"
1980    $itk_component(zCutButton) select
1981
1982    itk_component add zCutScale {
1983        ::scale $inner.zval -from 100 -to 0 \
1984            -width 10 -orient vertical -showvalue yes \
1985            -borderwidth 1 -highlightthickness 0 \
1986            -command [itcl::code $this EventuallySetCutplane z] \
1987            -variable [itcl::scope _settings(-zcutplaneposition)]
1988    } {
1989        usual
1990        ignore -borderwidth -highlightthickness
1991    }
1992    $itk_component(zCutScale) set 50
1993    $itk_component(zCutScale) configure -state disabled
1994    Rappture::Tooltip::for $itk_component(zCutScale) \
1995        "@[itcl::code $this Slice tooltip z]"
1996
1997    blt::table $inner \
1998        0,0 $inner.visible              -anchor w -pady 2 -cspan 4 \
1999        1,0 $inner.lighting             -anchor w -pady 2 -cspan 4 \
2000        2,0 $inner.opacity_l            -anchor w -pady 2 -cspan 3 \
2001        3,0 $inner.opacity              -fill x   -pady 2 -cspan 3 \
2002        4,0 $itk_component(xCutButton)  -anchor e -padx 2 -pady 2 \
2003        5,0 $itk_component(xCutScale)   -fill y \
2004        4,1 $itk_component(yCutButton)  -anchor e -padx 2 -pady 2 \
2005        5,1 $itk_component(yCutScale)   -fill y \
2006        4,2 $itk_component(zCutButton)  -anchor e -padx 2 -pady 2 \
2007        5,2 $itk_component(zCutScale)   -fill y
2008
2009    blt::table configure $inner r* c* -resize none
2010    blt::table configure $inner r5 c3 -resize expand
2011}
2012
2013#
2014#  camera --
2015#
2016itcl::body Rappture::VtkVolumeViewer::camera {option args} {
2017    switch -- $option {
2018        "show" {
2019            puts [array get _view]
2020        }
2021        "set" {
2022            set what [lindex $args 0]
2023            set x $_view($what)
2024            set code [catch { string is double $x } result]
2025            if { $code != 0 || !$result } {
2026                return
2027            }
2028            switch -- $what {
2029                "-ortho" {
2030                    if {$_view($what)} {
2031                        SendCmd "camera mode ortho"
2032                    } else {
2033                        SendCmd "camera mode persp"
2034                    }
2035                }
2036                "-xpan" - "-ypan" {
2037                    PanCamera
2038                }
2039                "-qx" - "-qy" - "-qz" - "-qw" {
2040                    set q [ViewToQuaternion]
2041                    $_arcball quaternion $q
2042                    EventuallyRotate $q
2043                }
2044                "-zoom" {
2045                    SendCmd "camera zoom $_view($what)"
2046                }
2047            }
2048        }
2049    }
2050}
2051
2052itcl::body Rappture::VtkVolumeViewer::GetVtkData { args } {
2053    set bytes ""
2054    foreach dataobj [get] {
2055        foreach comp [$dataobj components] {
2056            set tag $dataobj-$comp
2057            set contents [$dataobj vtkdata $comp]
2058            append bytes "$contents\n"
2059        }
2060    }
2061    return [list .vtk $bytes]
2062}
2063
2064itcl::body Rappture::VtkVolumeViewer::GetImage { args } {
2065    if { [image width $_image(download)] > 0 &&
2066         [image height $_image(download)] > 0 } {
2067        set bytes [$_image(download) data -format "jpeg -quality 100"]
2068        set bytes [Rappture::encoding::decode -as b64 $bytes]
2069        return [list .jpg $bytes]
2070    }
2071    return ""
2072}
2073
2074itcl::body Rappture::VtkVolumeViewer::BuildDownloadPopup { popup command } {
2075    Rappture::Balloon $popup \
2076        -title "[Rappture::filexfer::label downloadWord] as..."
2077    set inner [$popup component inner]
2078    label $inner.summary -text "" -anchor w
2079    radiobutton $inner.vtk_button -text "VTK data file" \
2080        -variable [itcl::scope _downloadPopup(format)] \
2081        -font "Helvetica 9 " \
2082        -value vtk
2083    Rappture::Tooltip::for $inner.vtk_button "Save as VTK data file."
2084    radiobutton $inner.image_button -text "Image File" \
2085        -variable [itcl::scope _downloadPopup(format)] \
2086        -value image
2087    Rappture::Tooltip::for $inner.image_button \
2088        "Save as digital image."
2089
2090    button $inner.ok -text "Save" \
2091        -highlightthickness 0 -pady 2 -padx 3 \
2092        -command $command \
2093        -compound left \
2094        -image [Rappture::icon download]
2095
2096    button $inner.cancel -text "Cancel" \
2097        -highlightthickness 0 -pady 2 -padx 3 \
2098        -command [list $popup deactivate] \
2099        -compound left \
2100        -image [Rappture::icon cancel]
2101
2102    blt::table $inner \
2103        0,0 $inner.summary -cspan 2  \
2104        1,0 $inner.vtk_button -anchor w -cspan 2 -padx { 4 0 } \
2105        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
2106        4,1 $inner.cancel -width .9i -fill y \
2107        4,0 $inner.ok -padx 2 -width .9i -fill y
2108    blt::table configure $inner r3 -height 4
2109    blt::table configure $inner r4 -pady 4
2110    raise $inner.image_button
2111    $inner.vtk_button invoke
2112    return $inner
2113}
2114
2115itcl::body Rappture::VtkVolumeViewer::SetObjectStyle { dataobj cname } {
2116    # Parse style string.
2117    set tag $dataobj-$cname
2118    array set style {
2119        -color      BCGYR
2120        -lighting   1
2121        -opacity    0.5
2122        -outline    0
2123        -visible    1
2124    }
2125    array set style [$dataobj style $cname]
2126    set _settings(-volumelighting) $style(-lighting)
2127    set _settings(-volumeopacity) [expr $style(-opacity) * 100.0]
2128    set _settings(-volumeoutline) $style(-outline)
2129    set _settings(-volumevisible) $style(-visible)
2130
2131    $itk_component(colormap) value $style(-color)
2132
2133    SendCmd "outline add $tag"
2134    SendCmd "outline color [Color2RGB $itk_option(-plotforeground)] $tag"
2135    SendCmd "outline visible $style(-outline) $tag"
2136
2137    SendCmd "$_cutplaneCmd add $tag"
2138    SendCmd "$_cutplaneCmd color [Color2RGB $itk_option(-plotforeground)] $tag"
2139    SendCmd "$_cutplaneCmd visible 0 $tag"
2140
2141    SendCmd "volume add $tag"
2142    SendCmd "volume lighting $style(-lighting) $tag"
2143    SendCmd "volume opacity $style(-opacity) $tag"
2144    SendCmd "volume visible $style(-visible) $tag"
2145    SetColormap $dataobj $cname
2146}
2147
2148itcl::body Rappture::VtkVolumeViewer::IsValidObject { dataobj } {
2149    if {[catch {$dataobj isa Rappture::Field} valid] != 0 || !$valid} {
2150        return 0
2151    }
2152    return 1
2153}
2154
2155# ----------------------------------------------------------------------
2156# USAGE: ReceiveLegend <colormap> <title> <vmin> <vmax> <size>
2157#
2158# Invoked automatically whenever the "legend" command comes in from
2159# the rendering server.  Indicates that binary image data with the
2160# specified <size> will follow.
2161# ----------------------------------------------------------------------
2162itcl::body Rappture::VtkVolumeViewer::ReceiveLegend { colormap title vmin vmax size } {
2163    if { [isconnected] } {
2164        set bytes [ReceiveBytes $size]
2165        if { ![info exists _image(legend)] } {
2166            set _image(legend) [image create photo]
2167        }
2168        $_image(legend) configure -data $bytes
2169        #puts stderr "read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
2170        if { [catch {DrawLegend} errs] != 0 } {
2171            puts stderr errs=$errs
2172        }
2173    }
2174}
2175
2176#
2177# DrawLegend --
2178#
2179itcl::body Rappture::VtkVolumeViewer::DrawLegend {} {
2180    set fname $_curFldName
2181    set c $itk_component(view)
2182    set w [winfo width $c]
2183    set h [winfo height $c]
2184    set font "Arial 8"
2185    set lineht [font metrics $font -linespace]
2186
2187    if { !$_settings(-legendvisible) } {
2188        $c delete legend
2189        return
2190    }
2191
2192    if { [info exists _fields($fname)] } {
2193        foreach { title units } $_fields($fname) break
2194        if { $units != "" } {
2195            set title [format "%s (%s)" $title $units]
2196        }
2197    } else {
2198        set title $fname
2199    }
2200
2201    set x [expr $w - 2]
2202    if { [$c find withtag "legend"] == "" } {
2203        set y 2
2204        $c create text $x $y \
2205            -anchor ne \
2206            -fill $itk_option(-plotforeground) -tags "title legend" \
2207            -font $font
2208        incr y $lineht
2209        $c create text $x $y \
2210            -anchor ne \
2211            -fill $itk_option(-plotforeground) -tags "vmax legend" \
2212            -font $font
2213        incr y $lineht
2214        $c create image $x $y \
2215            -anchor ne \
2216            -image $_image(legend) -tags "colormap legend"
2217        $c create text $x [expr {$h-2}] \
2218            -anchor se \
2219            -fill $itk_option(-plotforeground) -tags "vmin legend" \
2220            -font $font
2221        #$c bind colormap <Enter> [itcl::code $this EnterLegend %x %y]
2222        $c bind colormap <Leave> [itcl::code $this LeaveLegend]
2223        $c bind colormap <Motion> [itcl::code $this MotionLegend %x %y]
2224    }
2225    $c bind title <ButtonPress> [itcl::code $this Combo post]
2226    $c bind title <Enter> [itcl::code $this Combo activate]
2227    $c bind title <Leave> [itcl::code $this Combo deactivate]
2228    # Reset the item coordinates according the current size of the plot.
2229    $c itemconfigure title -text $title
2230    if { [info exists _limits($_curFldName)] } {
2231        foreach { vmin vmax } $_limits($_curFldName) break
2232        $c itemconfigure vmin -text [format %g $vmin]
2233        $c itemconfigure vmax -text [format %g $vmax]
2234    }
2235    set y 2
2236    $c coords title $x $y
2237    incr y $lineht
2238    $c coords vmax $x $y
2239    incr y $lineht
2240    $c coords colormap $x $y
2241    $c coords vmin $x [expr {$h - 2}]
2242}
2243
2244#
2245# EnterLegend --
2246#
2247itcl::body Rappture::VtkVolumeViewer::EnterLegend { x y } {
2248    SetLegendTip $x $y
2249}
2250
2251#
2252# MotionLegend --
2253#
2254itcl::body Rappture::VtkVolumeViewer::MotionLegend { x y } {
2255    Rappture::Tooltip::tooltip cancel
2256    set c $itk_component(view)
2257    SetLegendTip $x $y
2258}
2259
2260#
2261# LeaveLegend --
2262#
2263itcl::body Rappture::VtkVolumeViewer::LeaveLegend { } {
2264    Rappture::Tooltip::tooltip cancel
2265    .rappturetooltip configure -icon ""
2266}
2267
2268#
2269# SetLegendTip --
2270#
2271itcl::body Rappture::VtkVolumeViewer::SetLegendTip { x y } {
2272    set c $itk_component(view)
2273    set w [winfo width $c]
2274    set h [winfo height $c]
2275    set font "Arial 8"
2276    set lineht [font metrics $font -linespace]
2277
2278    set imgHeight [image height $_image(legend)]
2279    set coords [$c coords colormap]
2280    set imgX [expr $w - [image width $_image(legend)] - 2]
2281    set imgY [expr $y - 2 * ($lineht + 2)]
2282
2283    if { [info exists _fields($_title)] } {
2284        foreach { title units } $_fields($_title) break
2285        if { $units != "" } {
2286            set title [format "%s (%s)" $title $units]
2287        }
2288    } else {
2289        set title $_title
2290    }
2291    # Make a swatch of the selected color
2292    if { [catch { $_image(legend) get 10 $imgY } pixel] != 0 } {
2293        #puts stderr "out of range: $imgY"
2294        return
2295    }
2296    if { ![info exists _image(swatch)] } {
2297        set _image(swatch) [image create photo -width 24 -height 24]
2298    }
2299    set color [eval format "\#%02x%02x%02x" $pixel]
2300    $_image(swatch) put black  -to 0 0 23 23
2301    $_image(swatch) put $color -to 1 1 22 22
2302    .rappturetooltip configure -icon $_image(swatch)
2303
2304    # Compute the value of the point
2305    if { [info exists _limits($_curFldName)] } {
2306        foreach { vmin vmax } $_limits($_curFldName) break
2307        set t [expr 1.0 - (double($imgY) / double($imgHeight-1))]
2308        set value [expr $t * ($vmax - $vmin) + $vmin]
2309    } else {
2310        set value 0.0
2311    }
2312    set tipx [expr $x + 15]
2313    set tipy [expr $y - 5]
2314    Rappture::Tooltip::text $c "$title $value"
2315    Rappture::Tooltip::tooltip show $c +$tipx,+$tipy
2316}
2317
2318# ----------------------------------------------------------------------
2319# USAGE: Slice move x|y|z <newval>
2320#
2321# Called automatically when the user drags the slider to move the
2322# cut plane that slices 3D data.  Gets the current value from the
2323# slider and moves the cut plane to the appropriate point in the
2324# data set.
2325# ----------------------------------------------------------------------
2326itcl::body Rappture::VtkVolumeViewer::Slice {option args} {
2327    switch -- $option {
2328        "move" {
2329            set axis [lindex $args 0]
2330            set newval [lindex $args 1]
2331            if {[llength $args] != 2} {
2332                error "wrong # args: should be \"Slice move x|y|z newval\""
2333            }
2334            set newpos [expr {0.01*$newval}]
2335            SendCmd "$_cutplaneCmd slice $axis $newpos"
2336        }
2337        "tooltip" {
2338            set axis [lindex $args 0]
2339            set val [$itk_component(${axis}CutScale) get]
2340            return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
2341        }
2342        default {
2343            error "bad option \"$option\": should be axis, move, or tooltip"
2344        }
2345    }
2346}
2347
2348# ----------------------------------------------------------------------
2349# USAGE: _dropdown post
2350# USAGE: _dropdown unpost
2351# USAGE: _dropdown select
2352#
2353# Used internally to handle the dropdown list for this combobox.  The
2354# post/unpost options are invoked when the list is posted or unposted
2355# to manage the relief of the controlling button.  The select option
2356# is invoked whenever there is a selection from the list, to assign
2357# the value back to the gauge.
2358# ----------------------------------------------------------------------
2359itcl::body Rappture::VtkVolumeViewer::Combo {option} {
2360    set c $itk_component(view)
2361    switch -- $option {
2362        post {
2363            foreach { x1 y1 x2 y2 } [$c bbox title] break
2364            set x1 [expr [winfo width $itk_component(view)] - [winfo reqwidth $itk_component(fieldmenu)]]
2365            set x [expr $x1 + [winfo rootx $itk_component(view)]]
2366            set y [expr $y2 + [winfo rooty $itk_component(view)]]
2367            tk_popup $itk_component(fieldmenu) $x $y
2368        }
2369        activate {
2370            $c itemconfigure title -fill red
2371        }
2372        deactivate {
2373            $c itemconfigure title -fill $itk_option(-plotforeground)
2374        }
2375        invoke {
2376            $itk_component(field) value $_curFldLabel
2377            AdjustSetting -field
2378        }
2379        default {
2380            error "bad option \"$option\": should be post, unpost, select"
2381        }
2382    }
2383}
2384
2385itcl::body Rappture::VtkVolumeViewer::SetOrientation { side } {
2386    array set positions {
2387        front "1 0 0 0"
2388        back  "0 0 1 0"
2389        left  "0.707107 0 -0.707107 0"
2390        right "0.707107 0 0.707107 0"
2391        top   "0.707107 -0.707107 0 0"
2392        bottom "0.707107 0.707107 0 0"
2393    }
2394    foreach name { -qw -qx -qy -qz } value $positions($side) {
2395        set _view($name) $value
2396    }
2397    set q [ViewToQuaternion]
2398    $_arcball quaternion $q
2399    SendCmd "camera orient $q"
2400    SendCmd "camera reset"
2401    set _view(-xpan) 0
2402    set _view(-ypan) 0
2403    set _view(-zoom) 1.0
2404}
2405
2406#
2407# GetDatasetsWithComponent --
2408#
2409# Returns a list of all the datasets (known by the combination of
2410# their data object and component name) that match the given
2411# component name.  For example, this is used where we want to change
2412# the settings of volumes that have the current component.
2413#
2414itcl::body Rappture::VtkVolumeViewer::GetDatasetsWithComponent { cname } {
2415    if { ![info exists _volcomponents($cname)] } {
2416        return ""
2417    }
2418    return $_volcomponents($cname)
2419}
Note: See TracBrowser for help on using the repository browser.