source: branches/r9/gui/scripts/vtkvolumeviewer.tcl @ 4988

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