source: branches/1.3/gui/scripts/vtkvolumeviewer.tcl @ 5239

Last change on this file since 5239 was 5239, checked in by ldelgass, 5 years ago

more backporting on vtkvolume

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