source: trunk/gui/scripts/vtkvolumeviewer.tcl @ 3963

Last change on this file since 3963 was 3963, checked in by ldelgass, 11 years ago

Bring vtk volume viewer UI closer in line with new nanovis viewer, add settings
for blendmode, opacity. Note that some controls only work with certain volume
mappers in VTK (right now this is selected when the server is built).

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