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

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

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