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

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

merge r5347 from trunk

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