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

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

Add update in Rebuild to allow initial canvas size to be set. We don't want to
do this in the constructor since that can cause an error when the add method is
called before the constructor returns.

File size: 103.3 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: vtkvolumeviewer - Vtk volume viewer
4#
5#  It connects to the Vtk server running on a rendering farm,
6#  transmits data, and displays the results.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2014  HUBzero Foundation, LLC
10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13# ======================================================================
14package require Itk
15package require BLT
16#package require Img
17
18option add *VtkVolumeViewer.width 4i widgetDefault
19option add *VtkVolumeViewer*cursor crosshair widgetDefault
20option add *VtkVolumeViewer.height 4i widgetDefault
21option add *VtkVolumeViewer.foreground black widgetDefault
22option add *VtkVolumeViewer.controlBackground gray widgetDefault
23option add *VtkVolumeViewer.controlDarkBackground #999999 widgetDefault
24option add *VtkVolumeViewer.plotBackground black widgetDefault
25option add *VtkVolumeViewer.plotForeground white widgetDefault
26option add *VtkVolumeViewer.font \
27    -*-helvetica-medium-r-normal-*-12-* widgetDefault
28
29# must use this name -- plugs into Rappture::resources::load
30proc VtkVolumeViewer_init_resources {} {
31    Rappture::resources::register \
32        vtkvis_server Rappture::VtkVolumeViewer::SetServerList
33}
34
35itcl::class Rappture::VtkVolumeViewer {
36    inherit Rappture::VisViewer
37
38    itk_option define -plotforeground plotForeground Foreground ""
39    itk_option define -plotbackground plotBackground Background ""
40
41    constructor { hostlist args } {
42        Rappture::VisViewer::constructor $hostlist
43    } {
44        # defined below
45    }
46    destructor {
47        # defined below
48    }
49    public proc SetServerList { namelist } {
50        Rappture::VisViewer::SetServerList "vtkvis" $namelist
51    }
52    public method add {dataobj {settings ""}}
53    public method camera {option args}
54    public method delete {args}
55    public method disconnect {}
56    public method download {option args}
57    public method get {args}
58    public method isconnected {}
59    public method parameters {title args} {
60        # do nothing
61    }
62    public method scale {args}
63    public method updateTransferFunctions {}
64
65    private method AddNewMarker { x y }
66    private method BuildVolumeComponents {}
67    private method ComputeAlphamap { cname }
68    private method ComputeTransferFunction { cname }
69    private method GetColormap { cname color }
70    private method GetDatasetsWithComponent { cname }
71    private method HideAllMarkers {}
72    private method InitComponentSettings { cname }
73    private method ParseLevelsOption { cname levels }
74    private method ParseMarkersOption { cname markers }
75    private method RemoveMarker { x y }
76    private method ResetColormap { cname color }
77    private method SendTransferFunctions {}
78    private method SetInitialTransferFunction { dataobj cname }
79    private method SwitchComponent { cname }
80
81    private variable _alphamap
82    private variable _current "";       # Currently selected component
83    private variable _volcomponents   ; # Array of components found
84    private variable _componentsList   ; # List of component names
85    private variable _cname2style
86    private variable _cname2transferFunction
87    private variable _cname2defaultcolormap
88    private variable _cname2defaultalphamap
89
90    private variable _parsedFunction
91    private variable _transferFunctionEditors
92
93    # The following methods are only used by this class.
94    private method AdjustSetting {what {value ""}}
95    private method BuildAxisTab {}
96    private method BuildCameraTab {}
97    private method BuildCutplaneTab {}
98    private method BuildDownloadPopup { widget command }
99    private method BuildViewTab {}
100    private method BuildVolumeTab {}
101    private method DrawLegend {}
102    private method DrawLegendOld {}
103    private method Combo { option }
104    private method Connect {}
105    private method CurrentDatasets {args}
106    private method Disconnect {}
107    private method DoResize {}
108    private method DoRotate {}
109    private method EnterLegend { x y }
110    private method EventuallyResize { w h }
111    private method EventuallyRequestLegend {}
112    private method EventuallyRotate { q }
113    private method EventuallySetCutplane { axis args }
114    private method GetImage { args }
115    private method GetVtkData { args }
116    private method InitSettings { args  }
117    private method IsValidObject { dataobj }
118    private method LeaveLegend {}
119    private method MotionLegend { x y }
120    private method Pan {option x y}
121    private method PanCamera {}
122    private method Pick {x y}
123    private method QuaternionToView { q } {
124        foreach { _view(-qw) _view(-qx) _view(-qy) _view(-qz) } $q break
125    }
126    private method Rebuild {}
127    private method ReceiveDataset { args }
128    private method ReceiveImage { args }
129    private method ReceiveLegend { colormap title vmin vmax size }
130    private method RequestLegend {}
131    private method Rotate {option x y}
132    private method SetLegendTip { x y }
133    private method SetObjectStyle { dataobj comp }
134    private method SetOrientation { side }
135    private method Slice {option args}
136    private method ViewToQuaternion {} {
137        return [list $_view(-qw) $_view(-qx) $_view(-qy) $_view(-qz)]
138    }
139    private method Zoom {option}
140
141    private variable _arcball ""
142    private variable _dlist ""     ;    # list of data objects
143    private variable _obj2datasets
144    private variable _obj2ovride   ;    # maps dataobj => style override
145    private variable _datasets     ;    # contains all the dataobj-component
146                                   ;    # datasets in the server
147    private variable _colormaps    ;    # contains all the colormaps
148                                   ;    # in the server.
149    private variable _dataset2style    ;# maps dataobj-component to transfunc
150
151    private variable _click        ;    # info used for rotate operations
152    private variable _limits       ;    # autoscale min/max for all axes
153    private variable _view         ;    # view params for 3D view
154    private variable _settings
155    private variable _style;            # Array of current component styles.
156    private variable _initialStyle;     # Array of initial component styles.
157    private variable _reset 1;          # indicates if camera needs to be reset
158                                        # to starting position.
159
160    private variable _first ""     ;    # This is the topmost dataset.
161    private variable _start 0
162    private variable _title ""
163
164    common _downloadPopup;              # download options from popup
165    private common _hardcopy
166    private variable _width 0
167    private variable _height 0
168    private variable _resizePending 0
169    private variable _rotatePending 0
170    private variable _cutplanePending 0
171    private variable _legendPending 0
172    private variable _fields
173    private variable _curFldName ""
174    private variable _curFldLabel ""
175    private variable _colorMode "scalar"; #  Mode of colormap (vmag or scalar)
176    private variable _cutplaneCmd "imgcutplane"
177    private variable _allowMultiComponent 0
178    private variable _activeVolumes;   # Array of volumes that are active.
179}
180
181itk::usual VtkVolumeViewer {
182    keep -background -foreground -cursor -font
183    keep -plotbackground -plotforeground
184}
185
186# ----------------------------------------------------------------------
187# CONSTRUCTOR
188# ----------------------------------------------------------------------
189itcl::body Rappture::VtkVolumeViewer::constructor {hostlist args} {
190    package require vtk
191    set _serverType "vtkvis"
192
193    EnableWaitDialog 900
194
195    # Rebuild event
196    $_dispatcher register !rebuild
197    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
198
199    # Resize event
200    $_dispatcher register !resize
201    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
202
203    # Rotate event
204    $_dispatcher register !rotate
205    $_dispatcher dispatch $this !rotate "[itcl::code $this DoRotate]; list"
206
207    # Legend event
208    $_dispatcher register !legend
209    $_dispatcher dispatch $this !legend "[itcl::code $this RequestLegend]; list"
210
211    # X-Cutplane event
212    $_dispatcher register !xcutplane
213    $_dispatcher dispatch $this !xcutplane \
214        "[itcl::code $this AdjustSetting -xcutplaneposition]; list"
215
216    # Y-Cutplane event
217    $_dispatcher register !ycutplane
218    $_dispatcher dispatch $this !ycutplane \
219        "[itcl::code $this AdjustSetting -ycutplaneposition]; list"
220
221    # Z-Cutplane event
222    $_dispatcher register !zcutplane
223    $_dispatcher dispatch $this !zcutplane \
224        "[itcl::code $this AdjustSetting -zcutplaneposition]; list"
225
226    #
227    # Populate parser with commands handle incoming requests
228    #
229    $_parser alias image [itcl::code $this ReceiveImage]
230    $_parser alias dataset [itcl::code $this ReceiveDataset]
231    $_parser alias legend [itcl::code $this ReceiveLegend]
232
233    # Initialize the view to some default parameters.
234    array set _view {
235        -ortho           0
236        -qw              0.853553
237        -qx              -0.353553
238        -qy              0.353553
239        -qz              0.146447
240        -xpan            0
241        -ypan            0
242        -zoom            1.0
243    }
244    set _arcball [blt::arcball create 100 100]
245    $_arcball quaternion [ViewToQuaternion]
246
247    array set _settings {
248        -axesvisible                    1
249        -axisflymode                    static
250        -axislabels                     1
251        -axisminorticks                 1
252        -background                     black
253        -color                          "default"
254        -cutplanelighting               1
255        -cutplaneopacity                100
256        -cutplanesvisible               0
257        -legendvisible                  1
258        -volumeambient                  40
259        -volumeblendmode                composite
260        -volumediffuse                  60
261        -volumelighting                 1
262        -volumeopacity                  50
263        -volumeoutline                  0
264        -volumequality                  80
265        -volumespecularexponent         90
266        -volumespecularlevel            30
267        -volumethickness                350
268        -volumevisible                  1
269        -xcutplaneposition              50
270        -xcutplanevisible               1
271        -xgrid                          0
272        -ycutplaneposition              50
273        -ycutplanevisible               1
274        -ygrid                          0
275        -zcutplaneposition              50
276        -zcutplanevisible               1
277        -zgrid                          0
278    }
279
280    itk_component add view {
281        canvas $itk_component(plotarea).view \
282            -highlightthickness 0 -borderwidth 0
283    } {
284        usual
285        ignore -highlightthickness -borderwidth  -background
286    }
287
288    itk_component add fieldmenu {
289        menu $itk_component(plotarea).menu -bg black -fg white -relief flat \
290            -tearoff no
291    } {
292        usual
293        ignore -background -foreground -relief -tearoff
294    }
295    set c $itk_component(view)
296    bind $c <Configure> [itcl::code $this EventuallyResize %w %h]
297    bind $c <4> [itcl::code $this Zoom in 0.25]
298    bind $c <5> [itcl::code $this Zoom out 0.25]
299    bind $c <KeyPress-Left>  [list %W xview scroll 10 units]
300    bind $c <KeyPress-Right> [list %W xview scroll -10 units]
301    bind $c <KeyPress-Up>    [list %W yview scroll 10 units]
302    bind $c <KeyPress-Down>  [list %W yview scroll -10 units]
303    bind $c <Enter> "focus %W"
304    bind $c <Control-F1> [itcl::code $this ToggleConsole]
305
306    # Fixes the scrollregion in case we go off screen
307    $c configure -scrollregion [$c bbox all]
308
309    set _map(id) [$c create image 0 0 -anchor nw -image $_image(plot)]
310    set _map(cwidth) -1
311    set _map(cheight) -1
312    set _map(zoom) 1.0
313    set _map(original) ""
314
315    set f [$itk_component(main) component controls]
316    itk_component add reset {
317        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
318            -highlightthickness 0 \
319            -image [Rappture::icon reset-view] \
320            -command [itcl::code $this Zoom reset]
321    } {
322        usual
323        ignore -highlightthickness
324    }
325    pack $itk_component(reset) -side top -padx 2 -pady 2
326    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
327
328    itk_component add zoomin {
329        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
330            -highlightthickness 0 \
331            -image [Rappture::icon zoom-in] \
332            -command [itcl::code $this Zoom in]
333    } {
334        usual
335        ignore -highlightthickness
336    }
337    pack $itk_component(zoomin) -side top -padx 2 -pady 2
338    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
339
340    itk_component add zoomout {
341        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
342            -highlightthickness 0 \
343            -image [Rappture::icon zoom-out] \
344            -command [itcl::code $this Zoom out]
345    } {
346        usual
347        ignore -highlightthickness
348    }
349    pack $itk_component(zoomout) -side top -padx 2 -pady 2
350    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
351
352    itk_component add volume {
353        Rappture::PushButton $f.volume \
354            -onimage [Rappture::icon volume-on] \
355            -offimage [Rappture::icon volume-off] \
356            -variable [itcl::scope _settings(-volumevisible)] \
357            -command [itcl::code $this AdjustSetting -volumevisible]
358    }
359    $itk_component(volume) select
360    Rappture::Tooltip::for $itk_component(volume) \
361        "Don't display the volume"
362    pack $itk_component(volume) -padx 2 -pady 2
363
364    itk_component add cutplane {
365        Rappture::PushButton $f.cutplane \
366            -onimage [Rappture::icon cutbutton] \
367            -offimage [Rappture::icon cutbutton] \
368            -variable [itcl::scope _settings(-cutplanesvisible)] \
369            -command [itcl::code $this AdjustSetting -cutplanesvisible]
370    }
371    Rappture::Tooltip::for $itk_component(cutplane) \
372        "Show/Hide cutplanes"
373    pack $itk_component(cutplane) -padx 2 -pady 2
374
375    if { [catch {
376        BuildViewTab
377        BuildVolumeTab
378        BuildCutplaneTab
379        BuildAxisTab
380        BuildCameraTab
381    } errs] != 0 } {
382        puts stderr errs=$errs
383    }
384
385    # Legend
386    set _image(legend) [image create photo]
387    itk_component add legend {
388        canvas $itk_component(plotarea).legend -height 50 -highlightthickness 0
389    } {
390        usual
391        ignore -highlightthickness
392        rename -background -plotbackground plotBackground Background
393    }
394    bind $itk_component(legend) <KeyPress-Delete> \
395        [itcl::code $this RemoveMarker %x %y]
396    bind $itk_component(legend) <Enter> \
397        [list focus $itk_component(legend)]
398
399    # Hack around the Tk panewindow.  The problem is that the requested
400    # size of the 3d view isn't set until an image is retrieved from
401    # the server.  So the panewindow uses the tiny size.
402    set w 10000
403    pack forget $itk_component(view)
404    blt::table $itk_component(plotarea) \
405        0,0 $itk_component(view) -fill both -reqwidth $w  \
406        1,0 $itk_component(legend) -fill x
407    blt::table configure $itk_component(plotarea) r1 -resize none
408
409    # Bindings for rotation via mouse
410    bind $itk_component(view) <ButtonPress-1> \
411        [itcl::code $this Rotate click %x %y]
412    bind $itk_component(view) <B1-Motion> \
413        [itcl::code $this Rotate drag %x %y]
414    bind $itk_component(view) <ButtonRelease-1> \
415        [itcl::code $this Rotate release %x %y]
416
417    # Bindings for panning via mouse
418    bind $itk_component(view) <ButtonPress-2> \
419        [itcl::code $this Pan click %x %y]
420    bind $itk_component(view) <B2-Motion> \
421        [itcl::code $this Pan drag %x %y]
422    bind $itk_component(view) <ButtonRelease-2> \
423        [itcl::code $this Pan release %x %y]
424
425    #bind $itk_component(view) <ButtonRelease-3> \
426    #    [itcl::code $this Pick %x %y]
427
428    # Bindings for panning via keyboard
429    bind $itk_component(view) <KeyPress-Left> \
430        [itcl::code $this Pan set -10 0]
431    bind $itk_component(view) <KeyPress-Right> \
432        [itcl::code $this Pan set 10 0]
433    bind $itk_component(view) <KeyPress-Up> \
434        [itcl::code $this Pan set 0 -10]
435    bind $itk_component(view) <KeyPress-Down> \
436        [itcl::code $this Pan set 0 10]
437    bind $itk_component(view) <Shift-KeyPress-Left> \
438        [itcl::code $this Pan set -2 0]
439    bind $itk_component(view) <Shift-KeyPress-Right> \
440        [itcl::code $this Pan set 2 0]
441    bind $itk_component(view) <Shift-KeyPress-Up> \
442        [itcl::code $this Pan set 0 -2]
443    bind $itk_component(view) <Shift-KeyPress-Down> \
444        [itcl::code $this Pan set 0 2]
445
446    # Bindings for zoom via keyboard
447    bind $itk_component(view) <KeyPress-Prior> \
448        [itcl::code $this Zoom out]
449    bind $itk_component(view) <KeyPress-Next> \
450        [itcl::code $this Zoom in]
451
452    bind $itk_component(view) <Enter> "focus $itk_component(view)"
453
454    if {[string equal "x11" [tk windowingsystem]]} {
455        # Bindings for zoom via mouse
456        bind $itk_component(view) <4> [itcl::code $this Zoom out]
457        bind $itk_component(view) <5> [itcl::code $this Zoom in]
458    }
459
460    set _image(download) [image create photo]
461
462    eval itk_initialize $args
463    Connect
464}
465
466# ----------------------------------------------------------------------
467# DESTRUCTOR
468# ----------------------------------------------------------------------
469itcl::body Rappture::VtkVolumeViewer::destructor {} {
470    Disconnect
471    image delete $_image(plot)
472    image delete $_image(download)
473    catch { blt::arcball destroy $_arcball }
474}
475
476itcl::body Rappture::VtkVolumeViewer::DoResize {} {
477    if { $_width < 2 } {
478        set _width 500
479    }
480    if { $_height < 2 } {
481        set _height 500
482    }
483    set _start [clock clicks -milliseconds]
484    SendCmd "screen size $_width $_height"
485
486    EventuallyRequestLegend
487    set _resizePending 0
488}
489
490itcl::body Rappture::VtkVolumeViewer::DoRotate {} {
491    SendCmd "camera orient [ViewToQuaternion]"
492    set _rotatePending 0
493}
494
495itcl::body Rappture::VtkVolumeViewer::EventuallyResize { w h } {
496    set _width $w
497    set _height $h
498    $_arcball resize $w $h
499    if { !$_resizePending } {
500        set _resizePending 1
501        $_dispatcher event -after 400 !resize
502    }
503}
504
505itcl::body Rappture::VtkVolumeViewer::EventuallyRequestLegend {} {
506    if { !$_legendPending } {
507        set _legendPending 1
508        $_dispatcher event -idle !legend
509    }
510}
511
512set rotate_delay 100
513
514itcl::body Rappture::VtkVolumeViewer::EventuallyRotate { q } {
515    QuaternionToView $q
516    if { !$_rotatePending } {
517        set _rotatePending 1
518        global rotate_delay
519        $_dispatcher event -after $rotate_delay !rotate
520    }
521}
522
523itcl::body Rappture::VtkVolumeViewer::EventuallySetCutplane { axis args } {
524    if { !$_cutplanePending } {
525        set _cutplanePending 1
526        $_dispatcher event -after 100 !${axis}cutplane
527    }
528}
529
530# ----------------------------------------------------------------------
531# USAGE: add <dataobj> ?<settings>?
532#
533# Clients use this to add a data object to the plot.  The optional
534# <settings> are used to configure the plot.  Allowed settings are
535# -color, -brightness, -width, -linestyle, and -raise.
536# ----------------------------------------------------------------------
537itcl::body Rappture::VtkVolumeViewer::add {dataobj {settings ""}} {
538    if { ![IsValidObject $dataobj] } {
539        return;                         # Ignore invalid objects.
540    }
541    array set params {
542        -color auto
543        -width 1
544        -linestyle solid
545        -brightness 0
546        -raise 0
547        -description ""
548        -param ""
549        -type ""
550    }
551    array set params $settings
552    set params(-description) ""
553    set params(-param) ""
554    array set params $settings
555
556    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
557        # can't handle -autocolors yet
558        set params(-color) black
559    }
560    set pos [lsearch -exact $_dlist $dataobj]
561    if {$pos < 0} {
562        lappend _dlist $dataobj
563    }
564    set _obj2ovride($dataobj-color) $params(-color)
565    set _obj2ovride($dataobj-width) $params(-width)
566    set _obj2ovride($dataobj-raise) $params(-raise)
567    $_dispatcher event -idle !rebuild
568}
569
570
571# ----------------------------------------------------------------------
572# USAGE: delete ?<dataobj1> <dataobj2> ...?
573#
574#       Clients use this to delete a dataobj from the plot.  If no dataobjs
575#       are specified, then all dataobjs are deleted.  No data objects are
576#       deleted.  They are only removed from the display list.
577#
578# ----------------------------------------------------------------------
579itcl::body Rappture::VtkVolumeViewer::delete {args} {
580    if { [llength $args] == 0} {
581        set args $_dlist
582    }
583    # Delete all specified dataobjs
584    set changed 0
585    foreach dataobj $args {
586        set pos [lsearch -exact $_dlist $dataobj]
587        if { $pos < 0 } {
588            continue;                   # Don't know anything about it.
589        }
590        # Remove it from the dataobj list.
591        set _dlist [lreplace $_dlist $pos $pos]
592        array unset _obj2ovride $dataobj-*
593        array unset _settings $dataobj-*
594        set changed 1
595    }
596    # If anything changed, then rebuild the plot
597    if { $changed } {
598        $_dispatcher event -idle !rebuild
599    }
600}
601
602# ----------------------------------------------------------------------
603# USAGE: get ?-objects?
604# USAGE: get ?-visible?
605# USAGE: get ?-image view?
606#
607# Clients use this to query the list of objects being plotted, in
608# order from bottom to top of this result.  The optional "-image"
609# flag can also request the internal images being shown.
610# ----------------------------------------------------------------------
611itcl::body Rappture::VtkVolumeViewer::get {args} {
612    if {[llength $args] == 0} {
613        set args "-objects"
614    }
615
616    set op [lindex $args 0]
617    switch -- $op {
618        "-objects" {
619            # put the dataobj list in order according to -raise options
620            set dlist {}
621            foreach dataobj $_dlist {
622                if { ![IsValidObject $dataobj] } {
623                    continue
624                }
625                if {[info exists _obj2ovride($dataobj-raise)] &&
626                    $_obj2ovride($dataobj-raise)} {
627                    set dlist [linsert $dlist 0 $dataobj]
628                } else {
629                    lappend dlist $dataobj
630                }
631            }
632            return $dlist
633        }
634        "-visible" {
635            set dlist {}
636            foreach dataobj $_dlist {
637                if { ![IsValidObject $dataobj] } {
638                    continue
639                }
640                if { ![info exists _obj2ovride($dataobj-raise)] } {
641                    # No setting indicates that the object isn't visible.
642                    continue
643                }
644                # Otherwise use the -raise parameter to put the object to
645                # the front of the list.
646                if { $_obj2ovride($dataobj-raise) } {
647                    set dlist [linsert $dlist 0 $dataobj]
648                } else {
649                    lappend dlist $dataobj
650                }
651            }
652            return $dlist
653        }           
654        -image {
655            if {[llength $args] != 2} {
656                error "wrong # args: should be \"get -image view\""
657            }
658            switch -- [lindex $args end] {
659                view {
660                    return $_image(plot)
661                }
662                default {
663                    error "bad image name \"[lindex $args end]\": should be view"
664                }
665            }
666        }
667        default {
668            error "bad option \"$op\": should be -objects or -image"
669        }
670    }
671}
672
673# ----------------------------------------------------------------------
674# USAGE: scale ?<data1> <data2> ...?
675#
676# Sets the default limits for the overall plot according to the
677# limits of the data for all of the given <data> objects.  This
678# accounts for all objects--even those not showing on the screen.
679# Because of this, the limits are appropriate for all objects as
680# the user scans through data in the ResultSet viewer.
681# ----------------------------------------------------------------------
682itcl::body Rappture::VtkVolumeViewer::scale {args} {
683    array unset _limits
684    array unset _volcomponents
685    set _componentsList ""
686
687    foreach dataobj $args {
688        if { ![$dataobj isvalid] } {
689            continue;                     # Object doesn't contain valid data.
690        }
691        # Determine limits for each axis.
692        foreach axis { x y z } {
693            set lim [$dataobj limits $axis]
694            if { ![info exists _limits($axis)] } {
695                set _limits($axis) $lim
696                continue
697            }
698            foreach {min max} $lim break
699            foreach {amin amax} $_limits($axis) break
700            if { $amin > $min } {
701                set amin $min
702            }
703            if { $amax < $max } {
704                set amax $max
705            }
706            set _limits($axis) [list $amin $amax]
707        }
708        # Determine limits for each field.
709        foreach { fname lim } [$dataobj fieldlimits] {
710            if { ![info exists _limits($fname)] } {
711                set _limits($fname) $lim
712                continue
713            }
714            foreach {min max} $lim break
715            foreach {fmin fmax} $_limits($fname) break
716            if { $fmin > $min } {
717                set fmin $min
718            }
719            if { $fmax < $max } {
720                set fmax $max
721            }
722            set _limits($fname) [list $fmin $fmax]
723        }
724        # Get limits for each component.
725        foreach cname [$dataobj components] {
726            if { ![info exists _volcomponents($cname)] } {
727                lappend _componentsList $cname
728            }
729            lappend _volcomponents($cname) $dataobj-$cname
730            array unset limits
731            array set limits [$dataobj valueLimits $cname]
732            if { ![info exists _limits($cname)] } {
733                set _limits($cname) $limits(v)
734                continue
735            }
736            foreach {min max} $limits(v) break
737            foreach {vmin vmax} $_limits($cname) break
738            if { $vmin > $min } {
739                set vmin $min
740            }
741            if { $vmax < $max } {
742                set vmax $max
743            }
744            set _limits($cname) [list $vmin $vmax]
745        }
746    }
747    BuildVolumeComponents
748    updateTransferFunctions
749}
750
751# ----------------------------------------------------------------------
752# USAGE: download coming
753# USAGE: download controls <downloadCommand>
754# USAGE: download now
755#
756# Clients use this method to create a downloadable representation
757# of the plot.  Returns a list of the form {ext string}, where
758# "ext" is the file extension (indicating the type of data) and
759# "string" is the data itself.
760# ----------------------------------------------------------------------
761itcl::body Rappture::VtkVolumeViewer::download {option args} {
762    switch $option {
763        coming {
764            if {[catch {
765                blt::winop snap $itk_component(plotarea) $_image(download)
766            }]} {
767                $_image(download) configure -width 1 -height 1
768                $_image(download) put #000000
769            }
770        }
771        controls {
772            set popup .vtkviewerdownload
773            if { ![winfo exists .vtkviewerdownload] } {
774                set inner [BuildDownloadPopup $popup [lindex $args 0]]
775            } else {
776                set inner [$popup component inner]
777            }
778            set _downloadPopup(image_controls) $inner.image_frame
779            set num [llength [get]]
780            set num [expr {($num == 1) ? "1 result" : "$num results"}]
781            set word [Rappture::filexfer::label downloadWord]
782            $inner.summary configure -text "$word $num in the following format:"
783            update idletasks            ;# Fix initial sizes
784            return $popup
785        }
786        now {
787            set popup .vtkviewerdownload
788            if {[winfo exists .vtkviewerdownload]} {
789                $popup deactivate
790            }
791            switch -- $_downloadPopup(format) {
792                "image" {
793                    return [$this GetImage [lindex $args 0]]
794                }
795                "vtk" {
796                    return [$this GetVtkData [lindex $args 0]]
797                }
798            }
799            return ""
800        }
801        default {
802            error "bad option \"$option\": should be coming, controls, now"
803        }
804    }
805}
806
807# ----------------------------------------------------------------------
808# USAGE: Connect ?<host:port>,<host:port>...?
809#
810# Clients use this method to establish a connection to a new
811# server, or to reestablish a connection to the previous server.
812# Any existing connection is automatically closed.
813# ----------------------------------------------------------------------
814itcl::body Rappture::VtkVolumeViewer::Connect {} {
815    set _hosts [GetServerList "vtkvis"]
816    if { "" == $_hosts } {
817        return 0
818    }
819    set result [VisViewer::Connect $_hosts]
820    if { $result } {
821        if { $_reportClientInfo }  {
822            # Tell the server the viewer, hub, user and session.
823            # Do this immediately on connect before buffering any commands
824            global env
825
826            set info {}
827            set user "???"
828            if { [info exists env(USER)] } {
829                set user $env(USER)
830            }
831            set session "???"
832            if { [info exists env(SESSION)] } {
833                set session $env(SESSION)
834            }
835            lappend info "version" "$Rappture::version"
836            lappend info "build" "$Rappture::build"
837            lappend info "svnurl" "$Rappture::svnurl"
838            lappend info "installdir" "$Rappture::installdir"
839            lappend info "hub" [exec hostname]
840            lappend info "client" "vtkvolumeviewer"
841            lappend info "user" $user
842            lappend info "session" $session
843            SendCmd "clientinfo [list $info]"
844        }
845
846        set w [winfo width $itk_component(view)]
847        set h [winfo height $itk_component(view)]
848        EventuallyResize $w $h
849    }
850    return $result
851}
852
853#
854# isconnected --
855#
856#       Indicates if we are currently connected to the visualization server.
857#
858itcl::body Rappture::VtkVolumeViewer::isconnected {} {
859    return [VisViewer::IsConnected]
860}
861
862#
863# disconnect --
864#
865itcl::body Rappture::VtkVolumeViewer::disconnect {} {
866    Disconnect
867}
868
869#
870# Disconnect --
871#
872#       Clients use this method to disconnect from the current rendering
873#       server.
874#
875itcl::body Rappture::VtkVolumeViewer::Disconnect {} {
876    VisViewer::Disconnect
877
878    $_dispatcher cancel !rebuild
879    $_dispatcher cancel !resize
880    $_dispatcher cancel !rotate
881    $_dispatcher cancel !xcutplane
882    $_dispatcher cancel !ycutplane
883    $_dispatcher cancel !zcutplane
884    $_dispatcher cancel !legend
885    # disconnected -- no more data sitting on server
886    set _outbuf ""
887    array unset _datasets
888    array unset _data
889    array unset _colormaps
890    array unset _dataset2style
891    array unset _obj2datasets
892
893    array unset _cname2style
894    array unset _parsedFunction
895    array unset _cname2transferFunction
896
897    set _resizePending 0
898    set _rotatePending 0
899    set _cutplanePending 0
900    set _legendPending 0
901    set _reset 1
902}
903
904# ----------------------------------------------------------------------
905# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
906#
907# Invoked automatically whenever the "image" command comes in from
908# the rendering server.  Indicates that binary image data with the
909# specified <size> will follow.
910# ----------------------------------------------------------------------
911itcl::body Rappture::VtkVolumeViewer::ReceiveImage { args } {
912    array set info {
913        -token "???"
914        -bytes 0
915        -type image
916    }
917    array set info $args
918    set bytes [ReceiveBytes $info(-bytes)]
919    StopWaiting
920    if { $info(-type) == "image" } {
921        if 0 {
922            set f [open "last.ppm" "w"]
923            fconfigure $f -encoding binary
924            puts -nonewline $f $bytes
925            close $f
926        }
927        $_image(plot) configure -data $bytes
928        #puts stderr "[clock format [clock seconds]]: received image [image width $_image(plot)]x[image height $_image(plot)] image>"
929        if { $_start > 0 } {
930            set finish [clock clicks -milliseconds]
931            #puts stderr "round trip time [expr $finish -$_start] milliseconds"
932            set _start 0
933        }
934    } elseif { $info(type) == "print" } {
935        set tag $this-print-$info(-token)
936        set _hardcopy($tag) $bytes
937    }
938}
939
940#
941# ReceiveDataset --
942#
943itcl::body Rappture::VtkVolumeViewer::ReceiveDataset { args } {
944    if { ![isconnected] } {
945        return
946    }
947    set option [lindex $args 0]
948    switch -- $option {
949        "scalar" {
950            set option [lindex $args 1]
951            switch -- $option {
952                "world" {
953                    foreach { x y z value tag } [lrange $args 2 end] break
954                }
955                "pixel" {
956                    foreach { x y value tag } [lrange $args 2 end] break
957                }
958            }
959        }
960        "vector" {
961            set option [lindex $args 1]
962            switch -- $option {
963                "world" {
964                    foreach { x y z vx vy vz tag } [lrange $args 2 end] break
965                }
966                "pixel" {
967                    foreach { x y vx vy vz tag } [lrange $args 2 end] break
968                }
969            }
970        }
971        "names" {
972            foreach { name } [lindex $args 1] {
973                #puts stderr "Dataset: $name"
974            }
975        }
976        default {
977            error "unknown dataset option \"$option\" from server"
978        }
979    }
980}
981
982# ----------------------------------------------------------------------
983# USAGE: Rebuild
984#
985# Called automatically whenever something changes that affects the
986# data in the widget.  Clears any existing data and rebuilds the
987# widget to display new data.
988# ----------------------------------------------------------------------
989itcl::body Rappture::VtkVolumeViewer::Rebuild {} {
990    set w [winfo width $itk_component(view)]
991    set h [winfo height $itk_component(view)]
992    if { $w < 2 || $h < 2 } {
993        update
994        $_dispatcher event -idle !rebuild
995        return
996    }
997
998    # Turn on buffering of commands to the server.  We don't want to
999    # be preempted by a server disconnect/reconnect (which automatically
1000    # generates a new call to Rebuild).   
1001    StartBufferingCommands
1002
1003    if { $_width != $w || $_height != $h || $_reset } {
1004        set _width $w
1005        set _height $h
1006        $_arcball resize $w $h
1007        DoResize
1008    }
1009    if { $_reset } {
1010        #
1011        # Reset the camera and other view parameters
1012        #
1013        $_arcball quaternion [ViewToQuaternion]
1014        if {$_view(-ortho)} {
1015            SendCmd "camera mode ortho"
1016        } else {
1017            SendCmd "camera mode persp"
1018        }
1019        DoRotate
1020        PanCamera
1021        set _first ""
1022        InitSettings -background \
1023            -xgrid -ygrid -zgrid -axisflymode \
1024            -axesvisible -axislabels -axisminorticks
1025        StopBufferingCommands
1026        SendCmd "imgflush"
1027        StartBufferingCommands
1028     }
1029    set _first ""
1030
1031    SendCmd "dataset visible 0"
1032
1033    # No volumes are active (i.e. in the working set of displayed volumes).
1034    # A volume is always invisible if it's not in the working set.  A
1035    # volume in the working set may be visible/invisible depending upon the
1036    # global visibility value.
1037    array unset _activeVolumes
1038    foreach dataobj [get -objects] {
1039        if { [info exists _obj2ovride($dataobj-raise)] &&  $_first == "" } {
1040            set _first $dataobj
1041        }
1042        set _obj2datasets($dataobj) ""
1043        foreach comp [$dataobj components] {
1044            set tag $dataobj-$comp
1045            if { ![info exists _datasets($tag)] } {
1046                set bytes [$dataobj vtkdata $comp]
1047                if 0 {
1048                    set f [open /tmp/vtkvolume.vtk "w"]
1049                    fconfigure $f -translation binary -encoding binary
1050                    puts -nonewline $f $bytes
1051                    close $f
1052                }
1053                set length [string length $bytes]
1054                if { $_reportClientInfo }  {
1055                    set info {}
1056                    lappend info "tool_id"       [$dataobj hints toolid]
1057                    lappend info "tool_name"     [$dataobj hints toolname]
1058                    lappend info "tool_title"    [$dataobj hints tooltitle]
1059                    lappend info "tool_command"  [$dataobj hints toolcommand]
1060                    lappend info "tool_revision" [$dataobj hints toolrevision]
1061                    lappend info "dataset_label" [$dataobj hints label]
1062                    lappend info "dataset_size"  $length
1063                    lappend info "dataset_tag"   $tag
1064                    SendCmd "clientinfo [list $info]"
1065                }
1066                SendCmd "dataset add $tag data follows $length"
1067                append _outbuf $bytes
1068                set _datasets($tag) 1
1069                SetObjectStyle $dataobj $comp
1070            }
1071            lappend _obj2datasets($dataobj) $tag
1072            if { [info exists _obj2ovride($dataobj-raise)] } {
1073                SendCmd "volume visible 1 $tag"
1074            }
1075            break
1076        }
1077    }
1078    if {"" != $_first} {
1079        set location [$_first hints camera]
1080        if { $location != "" } {
1081            array set view $location
1082        }
1083
1084        foreach axis { x y z } {
1085            set label [$_first hints ${axis}label]
1086            if { $label != "" } {
1087                SendCmd [list axis name $axis $label]
1088            }
1089            set units [$_first hints ${axis}units]
1090            if { $units != "" } {
1091                SendCmd [list axis units $axis $units]
1092            }
1093        }
1094        $itk_component(field) choices delete 0 end
1095        $itk_component(fieldmenu) delete 0 end
1096        array unset _fields
1097        set _curFldName ""
1098        foreach cname [$_first components] {
1099            foreach fname [$_first fieldnames $cname] {
1100                if { [info exists _fields($fname)] } {
1101                    continue
1102                }
1103                foreach { label units components } \
1104                    [$_first fieldinfo $fname] break
1105                # Only scalar fields are valid
1106                if {$_allowMultiComponent || $components == 1} {
1107                    $itk_component(field) choices insert end "$fname" "$label"
1108                    $itk_component(fieldmenu) add radiobutton -label "$label" \
1109                        -value $label -variable [itcl::scope _curFldLabel] \
1110                        -selectcolor red \
1111                        -activebackground $itk_option(-plotbackground) \
1112                        -activeforeground $itk_option(-plotforeground) \
1113                        -font "Arial 8" \
1114                        -command [itcl::code $this Combo invoke]
1115                    set _fields($fname) [list $label $units $components]
1116                    if { $_curFldName == "" } {
1117                        set _curFldName $fname
1118                        set _curFldLabel $label
1119                    }
1120                }
1121            }
1122        }
1123        $itk_component(field) value $_curFldLabel
1124    }
1125
1126    InitSettings -color \
1127        -volumeambient -volumediffuse -volumespecularlevel \
1128        -volumespecularexponent -volumeblendmode -volumethickness \
1129        -volumelighting -volumeopacity -volumequality -volumeoutline -volumevisible \
1130        -cutplanesvisible \
1131        -xcutplaneposition -ycutplaneposition -zcutplaneposition \
1132        -xcutplanevisible -ycutplanevisible -zcutplanevisible
1133
1134    if { $_reset } {
1135        SendCmd "camera reset"
1136        SendCmd "camera zoom $_view(-zoom)"
1137        RequestLegend
1138        set _reset 0
1139    }
1140    # Actually write the commands to the server socket.  If it fails, we don't
1141    # care.  We're finished here.
1142    blt::busy hold $itk_component(hull)
1143    StopBufferingCommands
1144    blt::busy release $itk_component(hull)
1145}
1146
1147# ----------------------------------------------------------------------
1148# USAGE: CurrentDatasets ?-all -visible? ?dataobjs?
1149#
1150# Returns a list of server IDs for the current datasets being displayed.  This
1151# is normally a single ID, but it might be a list of IDs if the current data
1152# object has multiple components.
1153# ----------------------------------------------------------------------
1154itcl::body Rappture::VtkVolumeViewer::CurrentDatasets {args} {
1155    set flag [lindex $args 0]
1156    switch -- $flag {
1157        "-all" {
1158            if { [llength $args] > 1 } {
1159                error "CurrentDatasets: can't specify dataobj after \"-all\""
1160            }
1161            set dlist [get -objects]
1162        }
1163        "-visible" {
1164            if { [llength $args] > 1 } {
1165                set dlist {}
1166                set args [lrange $args 1 end]
1167                foreach dataobj $args {
1168                    if { [info exists _obj2ovride($dataobj-raise)] } {
1169                        lappend dlist $dataobj
1170                    }
1171                }
1172            } else {
1173                set dlist [get -visible]
1174            }
1175        }           
1176        default {
1177            set dlist $args
1178        }
1179    }
1180    set rlist ""
1181    foreach dataobj $dlist {
1182        foreach comp [$dataobj components] {
1183            set tag $dataobj-$comp
1184            if { [info exists _datasets($tag)] && $_datasets($tag) } {
1185                lappend rlist $tag
1186            }
1187        }
1188    }
1189    return $rlist
1190}
1191
1192# ----------------------------------------------------------------------
1193# USAGE: Zoom in
1194# USAGE: Zoom out
1195# USAGE: Zoom reset
1196#
1197# Called automatically when the user clicks on one of the zoom
1198# controls for this widget.  Changes the zoom for the current view.
1199# ----------------------------------------------------------------------
1200itcl::body Rappture::VtkVolumeViewer::Zoom {option} {
1201    switch -- $option {
1202        "in" {
1203            set _view(-zoom) [expr {$_view(-zoom)*1.25}]
1204            SendCmd "camera zoom $_view(-zoom)"
1205        }
1206        "out" {
1207            set _view(-zoom) [expr {$_view(-zoom)*0.8}]
1208            SendCmd "camera zoom $_view(-zoom)"
1209        }
1210        "reset" {
1211            array set _view {
1212                -qw      0.853553
1213                -qx      -0.353553
1214                -qy      0.353553
1215                -qz      0.146447
1216                -xpan    0
1217                -ypan    0
1218                -zoom    1.0
1219            }
1220            if { $_first != "" } {
1221                set location [$_first hints camera]
1222                if { $location != "" } {
1223                    array set _view $location
1224                }
1225            }
1226            $_arcball quaternion [ViewToQuaternion]
1227            DoRotate
1228            SendCmd "camera reset"
1229        }
1230    }
1231}
1232
1233itcl::body Rappture::VtkVolumeViewer::PanCamera {} {
1234    set x $_view(-xpan)
1235    set y $_view(-ypan)
1236    SendCmd "camera pan $x $y"
1237}
1238
1239# ----------------------------------------------------------------------
1240# USAGE: Rotate click <x> <y>
1241# USAGE: Rotate drag <x> <y>
1242# USAGE: Rotate release <x> <y>
1243#
1244# Called automatically when the user clicks/drags/releases in the
1245# plot area.  Moves the plot according to the user's actions.
1246# ----------------------------------------------------------------------
1247itcl::body Rappture::VtkVolumeViewer::Rotate {option x y} {
1248    switch -- $option {
1249        "click" {
1250            $itk_component(view) configure -cursor fleur
1251            set _click(x) $x
1252            set _click(y) $y
1253        }
1254        "drag" {
1255            if {[array size _click] == 0} {
1256                Rotate click $x $y
1257            } else {
1258                set w [winfo width $itk_component(view)]
1259                set h [winfo height $itk_component(view)]
1260                if {$w <= 0 || $h <= 0} {
1261                    return
1262                }
1263
1264                if {[catch {
1265                    # this fails sometimes for no apparent reason
1266                    set dx [expr {double($x-$_click(x))/$w}]
1267                    set dy [expr {double($y-$_click(y))/$h}]
1268                }]} {
1269                    return
1270                }
1271                if { $dx == 0 && $dy == 0 } {
1272                    return
1273                }
1274                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1275                EventuallyRotate $q
1276                set _click(x) $x
1277                set _click(y) $y
1278            }
1279        }
1280        "release" {
1281            Rotate drag $x $y
1282            $itk_component(view) configure -cursor ""
1283            catch {unset _click}
1284        }
1285        default {
1286            error "bad option \"$option\": should be click, drag, release"
1287        }
1288    }
1289}
1290
1291itcl::body Rappture::VtkVolumeViewer::Pick {x y} {
1292    foreach tag [CurrentDatasets -visible] {
1293        SendCmd "dataset getscalar pixel $x $y $tag"
1294    }
1295}
1296
1297# ----------------------------------------------------------------------
1298# USAGE: $this Pan click x y
1299#        $this Pan drag x y
1300#        $this Pan release x y
1301#
1302# Called automatically when the user clicks on one of the zoom
1303# controls for this widget.  Changes the zoom for the current view.
1304# ----------------------------------------------------------------------
1305itcl::body Rappture::VtkVolumeViewer::Pan {option x y} {
1306    switch -- $option {
1307        "set" {
1308            set w [winfo width $itk_component(view)]
1309            set h [winfo height $itk_component(view)]
1310            set x [expr $x / double($w)]
1311            set y [expr $y / double($h)]
1312            set _view(-xpan) [expr $_view(-xpan) + $x]
1313            set _view(-ypan) [expr $_view(-ypan) + $y]
1314            PanCamera
1315            return
1316        }
1317        "click" {
1318            set _click(x) $x
1319            set _click(y) $y
1320            $itk_component(view) configure -cursor hand1
1321        }
1322        "drag" {
1323            if { ![info exists _click(x)] } {
1324                set _click(x) $x
1325            }
1326            if { ![info exists _click(y)] } {
1327                set _click(y) $y
1328            }
1329            set w [winfo width $itk_component(view)]
1330            set h [winfo height $itk_component(view)]
1331            set dx [expr ($_click(x) - $x)/double($w)]
1332            set dy [expr ($_click(y) - $y)/double($h)]
1333            set _click(x) $x
1334            set _click(y) $y
1335            set _view(-xpan) [expr $_view(-xpan) - $dx]
1336            set _view(-ypan) [expr $_view(-ypan) - $dy]
1337            PanCamera
1338        }
1339        "release" {
1340            Pan drag $x $y
1341            $itk_component(view) configure -cursor ""
1342        }
1343        default {
1344            error "unknown option \"$option\": should set, click, drag, or release"
1345        }
1346    }
1347}
1348
1349# ----------------------------------------------------------------------
1350# USAGE: InitSettings <what> ?<value>?
1351#
1352# Used internally to update rendering settings whenever parameters
1353# change in the popup settings panel.  Sends the new settings off
1354# to the back end.
1355# ----------------------------------------------------------------------
1356itcl::body Rappture::VtkVolumeViewer::InitSettings { args } {
1357    foreach spec $args {
1358        if { [info exists _settings($_first${spec})] } {
1359            # Reset global setting with dataobj specific setting
1360            set _settings($spec) $_settings($_first${spec})
1361        }
1362        AdjustSetting $spec
1363    }
1364}
1365
1366#
1367# AdjustSetting --
1368#
1369#       Changes/updates a specific setting in the widget.  There are
1370#       usually user-setable option.  Commands are sent to the render
1371#       server.
1372#
1373itcl::body Rappture::VtkVolumeViewer::AdjustSetting {what {value ""}} {
1374    if { ![isconnected] } {
1375        if { $_reset } {
1376            # Just reconnect if we've been reset.
1377            Connect
1378        }
1379        return
1380    }
1381    switch -- $what {
1382        "-current" {
1383            set cname [$itk_component(volcomponents) value]
1384            SwitchComponent $cname
1385        }
1386        "-background" {
1387            set bgcolor [$itk_component(background) value]
1388            set _settings($what) $bgcolor
1389            array set fgcolors {
1390                "black" "white"
1391                "white" "black"
1392                "grey"  "black"
1393            }
1394            configure -plotbackground $bgcolor \
1395                -plotforeground $fgcolors($bgcolor)
1396            $itk_component(view) delete "legend"
1397            DrawLegend
1398        }
1399        "-volumeoutline" {
1400            set bool $_settings($what)
1401            SendCmd "outline visible 0"
1402            foreach tag [GetDatasetsWithComponent $_current] {
1403                SendCmd "outline visible $bool $tag"
1404            }
1405        }
1406        "-legendvisible" {
1407            set bool $_settings($what)
1408            set _settings($_current${what}) $bool
1409            if { $bool } {
1410                blt::table $itk_component(plotarea) \
1411                    1,0 $itk_component(legend) -fill x
1412            } else {
1413                blt::table forget $itk_component(legend)
1414            }
1415        }
1416        "-volumevisible" {
1417            set bool $_settings($what)
1418            set _settings($_current${what}) $bool
1419            # Only the data objects in the array _obj2ovride(*-raise) are
1420            # in the working set and can be displayed on screen. The global
1421            # volume control determines whether they are visible.
1422            #
1423            # Note: The use of the current component is a hold over from
1424            #       nanovis.  If we can't display more than one volume,
1425            #       we don't have to limit the effects to a specific
1426            #       component.
1427            foreach tag [GetDatasetsWithComponent $_current] {
1428                foreach {dataobj cname} [split $tag -] break
1429                if { [info exists _obj2ovride($dataobj-raise)] } {
1430                    SendCmd "volume visible $bool $tag"
1431                }
1432            }
1433            if { $bool } {
1434                Rappture::Tooltip::for $itk_component(volume) \
1435                    "Hide the volume"
1436            } else {
1437                Rappture::Tooltip::for $itk_component(volume) \
1438                    "Show the volume"
1439            }
1440        }
1441        "-volumeblendmode" {
1442            set val [$itk_component(blendmode) value]
1443            set mode [$itk_component(blendmode) translate $val]
1444            set _settings($what) $mode
1445            set _settings($_current${what}) $mode
1446            foreach tag [GetDatasetsWithComponent $_current] {
1447                SendCmd "volume blendmode $mode $tag"
1448            }
1449        }
1450        "-volumeambient" {
1451            # Other parts of the code use the -volumeambient setting to
1452            # tell if the component settings have been initialized
1453            if { ![info exists _settings($_current${what})] } {
1454                InitComponentSettings $_current
1455            }
1456            set val $_settings($what)
1457            set _settings($_current${what}) $val
1458            set ambient [expr {0.01*$val}]
1459            foreach tag [GetDatasetsWithComponent $_current] {
1460                SendCmd "volume shading ambient $ambient $tag"
1461            }
1462        }
1463        "-volumediffuse" {
1464            set val $_settings($what)
1465            set _settings($_current${what}) $val
1466            set diffuse [expr {0.01*$val}]
1467            foreach tag [GetDatasetsWithComponent $_current] {
1468                SendCmd "volume shading diffuse $diffuse $tag"
1469            }
1470        }
1471        "-volumespecularlevel" - "-volumespecularexponent" {
1472            set val $_settings($what)
1473            set _settings($_current${what}) $val
1474            set level [expr {0.01*$val}]
1475            set exp $_settings($what)
1476            foreach tag [GetDatasetsWithComponent $_current] {
1477                SendCmd "volume shading specular $level $exp $tag"
1478            }
1479        }
1480        "-volumelighting" {
1481            set bool $_settings($what)
1482            set _settings($_current${what}) $bool
1483            foreach tag [GetDatasetsWithComponent $_current] {
1484                SendCmd "volume lighting $bool $tag"
1485            }
1486        }
1487        "-volumeopacity" {
1488            set val $_settings($what)
1489            set _settings($_current${what}) $val
1490            set val [expr {0.01*$val}]
1491            foreach tag [GetDatasetsWithComponent $_current] {
1492                SendCmd "volume opacity $val $tag"
1493            }
1494        }
1495        "-volumequality" {
1496            set val $_settings($what)
1497            set _settings($_current${what}) $val
1498            set val [expr {0.01*$val}]
1499            foreach tag [GetDatasetsWithComponent $_current] {
1500                SendCmd "volume quality $val $tag"
1501            }
1502        }
1503        "-axesvisible" {
1504            set bool $_settings($what)
1505            SendCmd "axis visible all $bool"
1506        }
1507        "-axislabels" {
1508            set bool $_settings($what)
1509            SendCmd "axis labels all $bool"
1510        }
1511        "-axisminorticks" {
1512            set bool $_settings($what)
1513            SendCmd "axis minticks all $bool"
1514        }
1515        "-xgrid" - "-ygrid" - "-zgrid" {
1516            set axis [string range $what 1 1]
1517            set bool $_settings($what)
1518            SendCmd "axis grid $axis $bool"
1519        }
1520        "-axisflymode" {
1521            set mode [$itk_component(axismode) value]
1522            set mode [$itk_component(axismode) translate $mode]
1523            set _settings($what) $mode
1524            SendCmd "axis flymode $mode"
1525        }
1526        "-cutplanesvisible" {
1527            set bool $_settings($what)
1528            foreach dataset [CurrentDatasets -visible] {
1529                SendCmd "$_cutplaneCmd visible $bool $dataset"
1530            }
1531        }
1532        "-cutplanelighting" {
1533            set bool $_settings($what)
1534            foreach dataset [CurrentDatasets -visible] {
1535                if {$_cutplaneCmd != "imgcutplane"} {
1536                    SendCmd "$_cutplaneCmd lighting $bool $dataset"
1537                } else {
1538                    if {$bool} {
1539                        set ambient 0.0
1540                        set diffuse 1.0
1541                    } else {
1542                        set ambient 1.0
1543                        set diffuse 0.0
1544                    }
1545                    SendCmd "imgcutplane material $ambient $diffuse $dataset"
1546                }
1547            }
1548        }
1549        "-cutplaneopacity" {
1550            set val $_settings($what)
1551            set sval [expr { 0.01 * double($val) }]
1552            foreach dataset [CurrentDatasets -visible] {
1553                SendCmd "$_cutplaneCmd opacity $sval $dataset"
1554            }
1555        }
1556        "-xcutplanevisible" - "-ycutplanevisible" - "-zcutplanevisible" {
1557            set axis [string range $what 1 1]
1558            set bool $_settings($what)
1559            if { $bool } {
1560                $itk_component(${axis}CutScale) configure -state normal \
1561                    -troughcolor white
1562            } else {
1563                $itk_component(${axis}CutScale) configure -state disabled \
1564                    -troughcolor grey82
1565            }
1566            foreach dataset [CurrentDatasets -visible] {
1567                SendCmd "$_cutplaneCmd axis $axis $bool $dataset"
1568            }
1569        }
1570        "-xcutplaneposition" - "-ycutplaneposition" - "-zcutplaneposition" {
1571            set axis [string range $what 1 1]
1572            set pos [expr $_settings($what) * 0.01]
1573            foreach dataset [CurrentDatasets -visible] {
1574                SendCmd "$_cutplaneCmd slice ${axis} ${pos} $dataset"
1575            }
1576            set _cutplanePending 0
1577        }
1578        "-volumethickness" {
1579            set _settings($_current${what}) $_settings($what)
1580            updateTransferFunctions
1581        }
1582        "-color" {
1583            set color [$itk_component(colormap) value]
1584            set _settings($what) $color
1585            set _settings($_current${what}) $color
1586            ResetColormap $_current $color
1587        }
1588        "-field" {
1589            set label [$itk_component(field) value]
1590            set fname [$itk_component(field) translate $label]
1591            set _settings($what) $fname
1592            if { [info exists _fields($fname)] } {
1593                foreach { label units components } $_fields($fname) break
1594                if { !$_allowMultiComponent && $components > 1 } {
1595                    puts stderr "Can't use a vector field in a volume"
1596                    return
1597                } else {
1598                    if { $components > 1 } {
1599                        set _colorMode vmag
1600                    } else {
1601                        set _colorMode scalar
1602                    }
1603                }
1604                set _curFldName $fname
1605                set _curFldLabel $label
1606            } else {
1607                puts stderr "unknown field \"$fname\""
1608                return
1609            }
1610            foreach dataset [CurrentDatasets -visible $_first] {
1611                #SendCmd "$_cutplaneCmd colormode $_colorMode $_curFldName $dataset"
1612                SendCmd "dataset scalar $_curFldName $dataset"
1613            }
1614            SendCmd "camera reset"
1615            DrawLegend
1616        }
1617        default {
1618            error "don't know how to fix $what"
1619        }
1620    }
1621}
1622
1623#
1624# RequestLegend --
1625#
1626#       Request a new legend from the server.  The size of the legend
1627#       is determined from the height of the canvas.
1628#
1629itcl::body Rappture::VtkVolumeViewer::RequestLegend {} {
1630    set _legendPending 0
1631    set font "Arial 8"
1632    set lineht [font metrics $itk_option(-font) -linespace]
1633    set c $itk_component(legend)
1634    set w [winfo width $c]
1635    set h [winfo height $c]
1636    set h [expr {$h-$lineht-20}]
1637    set w [expr {$w-20}]
1638    # Set the legend on the first volume dataset.
1639    foreach dataset [CurrentDatasets -visible $_first] {
1640        foreach {dataobj comp} [split $dataset -] break
1641        if { [info exists _dataset2style($dataset)] } {
1642            SendCmd \
1643                "legend2 $_dataset2style($dataset) $w $h"
1644                #"legend $_dataset2style($dataset) $_colorMode $_curFldName {} $w $h 0"
1645            break;
1646        }
1647    }
1648}
1649
1650# ----------------------------------------------------------------------
1651# CONFIGURATION OPTION: -plotbackground
1652# ----------------------------------------------------------------------
1653itcl::configbody Rappture::VtkVolumeViewer::plotbackground {
1654    if { [isconnected] } {
1655        set color $itk_option(-plotbackground)
1656        set rgb [Color2RGB $color]
1657        SendCmd "screen bgcolor $rgb"
1658        $itk_component(legend) configure -background $color
1659    }
1660}
1661
1662# ----------------------------------------------------------------------
1663# CONFIGURATION OPTION: -plotforeground
1664# ----------------------------------------------------------------------
1665itcl::configbody Rappture::VtkVolumeViewer::plotforeground {
1666    if { [isconnected] } {
1667        set color $itk_option(-plotforeground)
1668        set rgb [Color2RGB $color]
1669        SendCmd "axis color all $rgb"
1670        SendCmd "outline color $rgb"
1671        SendCmd "$_cutplaneCmd color $rgb"
1672        $itk_component(legend) itemconfigure labels -fill $color
1673        $itk_component(legend) itemconfigure limits -fill $color
1674    }
1675}
1676
1677itcl::body Rappture::VtkVolumeViewer::BuildViewTab {} {
1678    set fg [option get $itk_component(hull) font Font]
1679    #set bfg [option get $itk_component(hull) boldFont Font]
1680
1681    set inner [$itk_component(main) insert end \
1682        -title "View Settings" \
1683        -icon [Rappture::icon wrench]]
1684    $inner configure -borderwidth 4
1685
1686    checkbutton $inner.axes \
1687        -text "Axes" \
1688        -variable [itcl::scope _settings(-axesvisible)] \
1689        -command [itcl::code $this AdjustSetting -axesvisible] \
1690        -font "Arial 9"
1691
1692    checkbutton $inner.outline \
1693        -text "Outline" \
1694        -variable [itcl::scope _settings(-volumeoutline)] \
1695        -command [itcl::code $this AdjustSetting -volumeoutline] \
1696        -font "Arial 9"
1697
1698    checkbutton $inner.legend \
1699        -text "Legend" \
1700        -variable [itcl::scope _settings(-legendvisible)] \
1701        -command [itcl::code $this AdjustSetting -legendvisible] \
1702        -font "Arial 9"
1703
1704    checkbutton $inner.volume \
1705        -text "Volume" \
1706        -variable [itcl::scope _settings(-volumevisible)] \
1707        -command [itcl::code $this AdjustSetting -volumevisible] \
1708        -font "Arial 9"
1709
1710    label $inner.background_l -text "Background" -font "Arial 9"
1711    itk_component add background {
1712        Rappture::Combobox $inner.background -width 10 -editable no
1713    }
1714    $inner.background choices insert end \
1715        "black"              "black"            \
1716        "white"              "white"            \
1717        "grey"               "grey"             
1718
1719    $itk_component(background) value $_settings(-background)
1720    bind $inner.background <<Value>> \
1721        [itcl::code $this AdjustSetting -background]
1722
1723    blt::table $inner \
1724        0,0 $inner.axes  -cspan 2 -anchor w \
1725        1,0 $inner.outline  -cspan 2 -anchor w \
1726        2,0 $inner.volume  -cspan 2 -anchor w \
1727        3,0 $inner.legend  -cspan 2 -anchor w \
1728        4,0 $inner.background_l       -anchor e -pady 2 \
1729        4,1 $inner.background                   -fill x \
1730
1731    blt::table configure $inner r* -resize none
1732    blt::table configure $inner r5 -resize expand
1733}
1734
1735itcl::body Rappture::VtkVolumeViewer::BuildVolumeTab {} {
1736    set font [option get $itk_component(hull) font Font]
1737    #set bfont [option get $itk_component(hull) boldFont Font]
1738
1739    set inner [$itk_component(main) insert end \
1740        -title "Volume Settings" \
1741        -icon [Rappture::icon volume-on]]
1742    $inner configure -borderwidth 4
1743
1744    label $inner.volcomponents_l -text "Component" -font $font
1745    itk_component add volcomponents {
1746        Rappture::Combobox $inner.volcomponents -editable no
1747    }
1748    bind $inner.volcomponents <<Value>> \
1749        [itcl::code $this AdjustSetting -current]
1750
1751    checkbutton $inner.visibility \
1752        -text "Visible" \
1753        -font $font \
1754        -variable [itcl::scope _settings(-volumevisible)] \
1755        -command [itcl::code $this AdjustSetting -volumevisible]
1756
1757    label $inner.lighting_l \
1758        -text "Lighting / Material Properties" \
1759        -font "Arial 9 bold"
1760
1761    checkbutton $inner.lighting \
1762        -text "Enable Lighting" \
1763        -font $font \
1764        -variable [itcl::scope _settings(-volumelighting)] \
1765        -command [itcl::code $this AdjustSetting -volumelighting]
1766
1767    label $inner.ambient_l \
1768        -text "Ambient" \
1769        -font $font
1770    ::scale $inner.ambient -from 0 -to 100 -orient horizontal \
1771        -variable [itcl::scope _settings(-volumeambient)] \
1772        -showvalue off \
1773        -command [itcl::code $this AdjustSetting -volumeambient] \
1774        -troughcolor grey92
1775
1776    label $inner.diffuse_l -text "Diffuse" -font $font
1777    ::scale $inner.diffuse -from 0 -to 100 -orient horizontal \
1778        -variable [itcl::scope _settings(-volumediffuse)] \
1779        -showvalue off \
1780        -command [itcl::code $this AdjustSetting -volumediffuse] \
1781        -troughcolor grey92
1782
1783    label $inner.specularLevel_l -text "Specular" -font $font
1784    ::scale $inner.specularLevel -from 0 -to 100 -orient horizontal \
1785        -variable [itcl::scope _settings(-volumespecularlevel)] \
1786        -showvalue off \
1787        -command [itcl::code $this AdjustSetting -volumespecularlevel] \
1788        -troughcolor grey92
1789
1790    label $inner.specularExponent_l -text "Shininess" -font $font
1791    ::scale $inner.specularExponent -from 10 -to 128 -orient horizontal \
1792        -variable [itcl::scope _settings(-volumespecularexponent)] \
1793        -showvalue off \
1794        -command [itcl::code $this AdjustSetting -volumespecularexponent] \
1795        -troughcolor grey92
1796
1797    label $inner.opacity_l -text "Opacity" -font $font
1798    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1799        -variable [itcl::scope _settings(-volumeopacity)] \
1800        -showvalue off \
1801        -command [itcl::code $this AdjustSetting -volumeopacity] \
1802        -troughcolor grey92
1803
1804    label $inner.quality_l -text "Quality" -font $font
1805    ::scale $inner.quality -from 0 -to 100 -orient horizontal \
1806        -variable [itcl::scope _settings(-volumequality)] \
1807        -showvalue off \
1808        -command [itcl::code $this AdjustSetting -volumequality] \
1809        -troughcolor grey92
1810
1811    label $inner.field_l -text "Field" -font $font
1812    itk_component add field {
1813        Rappture::Combobox $inner.field -editable no
1814    }
1815    bind $inner.field <<Value>> \
1816        [itcl::code $this AdjustSetting -field]
1817
1818    label $inner.transferfunction_l \
1819        -text "Transfer Function" -font "Arial 9 bold"
1820
1821    label $inner.thin -text "Thin" -font $font
1822    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
1823        -variable [itcl::scope _settings(-volumethickness)] \
1824        -showvalue off \
1825        -command [itcl::code $this AdjustSetting -volumethickness] \
1826        -troughcolor grey92
1827
1828    label $inner.thick -text "Thick" -font $font
1829    $inner.thickness set $_settings(-volumethickness)
1830
1831    label $inner.colormap_l -text "Colormap" -font $font
1832    itk_component add colormap {
1833        Rappture::Combobox $inner.colormap -width 10 -editable no
1834    }
1835    $inner.colormap choices insert end [GetColormapList -includeDefault]
1836
1837    bind $inner.colormap <<Value>> \
1838        [itcl::code $this AdjustSetting -color]
1839    $itk_component(colormap) value $_settings(-color)
1840
1841    label $inner.blendmode_l -text "Blend Mode" -font $font
1842    itk_component add blendmode {
1843        Rappture::Combobox $inner.blendmode -editable no
1844    }
1845    $inner.blendmode choices insert end \
1846        "composite"          "Composite"         \
1847        "max_intensity"      "Maximum Intensity" \
1848        "additive"           "Additive"
1849
1850    $itk_component(blendmode) value \
1851        "[$itk_component(blendmode) label $_settings(-volumeblendmode)]"
1852    bind $inner.blendmode <<Value>> \
1853        [itcl::code $this AdjustSetting -volumeblendmode]
1854
1855    blt::table $inner \
1856        0,0 $inner.volcomponents_l -anchor e -cspan 2 \
1857        0,2 $inner.volcomponents             -cspan 3 -fill x \
1858        1,0 $inner.field_l   -anchor e -cspan 2  \
1859        1,2 $inner.field               -cspan 3 -fill x \
1860        2,0 $inner.lighting_l -anchor w -cspan 4 \
1861        3,1 $inner.lighting   -anchor w -cspan 3 \
1862        4,1 $inner.ambient_l       -anchor e -pady 2 \
1863        4,2 $inner.ambient                   -cspan 3 -fill x \
1864        5,1 $inner.diffuse_l       -anchor e -pady 2 \
1865        5,2 $inner.diffuse                   -cspan 3 -fill x \
1866        6,1 $inner.specularLevel_l -anchor e -pady 2 \
1867        6,2 $inner.specularLevel             -cspan 3 -fill x \
1868        7,1 $inner.specularExponent_l -anchor e -pady 2 \
1869        7,2 $inner.specularExponent          -cspan 3 -fill x \
1870        8,1 $inner.visibility    -anchor w -cspan 3 \
1871        9,1 $inner.quality_l -anchor e -pady 2 \
1872        9,2 $inner.quality                     -cspan 3 -fill x \
1873        10,0 $inner.transferfunction_l -anchor w              -cspan 4 \
1874        11,1 $inner.opacity_l -anchor e -pady 2 \
1875        11,2 $inner.opacity                    -cspan 3 -fill x \
1876        12,1 $inner.colormap_l -anchor e  \
1877        12,2 $inner.colormap                 -padx 2 -cspan 3 -fill x \
1878        13,1 $inner.blendmode_l -anchor e  \
1879        13,2 $inner.blendmode               -padx 2 -cspan 3 -fill x \
1880        14,1 $inner.thin             -anchor e \
1881        14,2 $inner.thickness                 -cspan 2 -fill x \
1882        14,4 $inner.thick -anchor w 
1883
1884    blt::table configure $inner r* c* -resize none
1885    blt::table configure $inner r* -pady { 2 0 }
1886    blt::table configure $inner c2 c3 r15 -resize expand
1887    blt::table configure $inner c0 -width .1i
1888}
1889
1890itcl::body Rappture::VtkVolumeViewer::BuildAxisTab {} {
1891    set fg [option get $itk_component(hull) font Font]
1892    #set bfg [option get $itk_component(hull) boldFont Font]
1893
1894    set inner [$itk_component(main) insert end \
1895        -title "Axis Settings" \
1896        -icon [Rappture::icon axis2]]
1897    $inner configure -borderwidth 4
1898
1899    checkbutton $inner.visible \
1900        -text "Axes" \
1901        -variable [itcl::scope _settings(-axesvisible)] \
1902        -command [itcl::code $this AdjustSetting -axesvisible] \
1903        -font "Arial 9"
1904
1905    checkbutton $inner.labels \
1906        -text "Axis Labels" \
1907        -variable [itcl::scope _settings(-axislabels)] \
1908        -command [itcl::code $this AdjustSetting -axislabels] \
1909        -font "Arial 9"
1910    label $inner.grid_l -text "Grid" -font "Arial 9"
1911    checkbutton $inner.xgrid \
1912        -text "X" \
1913        -variable [itcl::scope _settings(-xgrid)] \
1914        -command [itcl::code $this AdjustSetting -xgrid] \
1915        -font "Arial 9"
1916    checkbutton $inner.ygrid \
1917        -text "Y" \
1918        -variable [itcl::scope _settings(-ygrid)] \
1919        -command [itcl::code $this AdjustSetting -ygrid] \
1920        -font "Arial 9"
1921    checkbutton $inner.zgrid \
1922        -text "Z" \
1923        -variable [itcl::scope _settings(-zgrid)] \
1924        -command [itcl::code $this AdjustSetting -zgrid] \
1925        -font "Arial 9"
1926    checkbutton $inner.minorticks \
1927        -text "Minor Ticks" \
1928        -variable [itcl::scope _settings(-axisminorticks)] \
1929        -command [itcl::code $this AdjustSetting -axisminorticks] \
1930        -font "Arial 9"
1931
1932    label $inner.mode_l -text "Mode" -font "Arial 9"
1933
1934    itk_component add axismode {
1935        Rappture::Combobox $inner.mode -width 10 -editable no
1936    }
1937    $inner.mode choices insert end \
1938        "static_triad"    "static" \
1939        "closest_triad"   "closest" \
1940        "furthest_triad"  "farthest" \
1941        "outer_edges"     "outer"         
1942    $itk_component(axismode) value $_settings(-axisflymode)
1943    bind $inner.mode <<Value>> [itcl::code $this AdjustSetting -axisflymode]
1944
1945    blt::table $inner \
1946        0,0 $inner.visible -anchor w -cspan 4 \
1947        1,0 $inner.labels  -anchor w -cspan 4 \
1948        2,0 $inner.minorticks  -anchor w -cspan 4 \
1949        4,0 $inner.grid_l  -anchor w \
1950        4,1 $inner.xgrid   -anchor w \
1951        4,2 $inner.ygrid   -anchor w \
1952        4,3 $inner.zgrid   -anchor w \
1953        5,0 $inner.mode_l  -anchor w -padx { 2 0 } \
1954        5,1 $inner.mode    -fill x   -cspan 3
1955
1956    blt::table configure $inner r* c* -resize none
1957    blt::table configure $inner r7 c6 -resize expand
1958    blt::table configure $inner r3 -height 0.125i
1959}
1960
1961itcl::body Rappture::VtkVolumeViewer::BuildCameraTab {} {
1962    set inner [$itk_component(main) insert end \
1963        -title "Camera Settings" \
1964        -icon [Rappture::icon camera]]
1965    $inner configure -borderwidth 4
1966
1967    label $inner.view_l -text "view" -font "Arial 9"
1968    set f [frame $inner.view]
1969    foreach side { front back left right top bottom } {
1970        button $f.$side  -image [Rappture::icon view$side] \
1971            -command [itcl::code $this SetOrientation $side]
1972        Rappture::Tooltip::for $f.$side "Change the view to $side"
1973        pack $f.$side -side left
1974    }
1975    blt::table $inner \
1976        0,0 $inner.view_l -anchor e -pady 2 \
1977        0,1 $inner.view -anchor w -pady 2
1978    blt::table configure $inner r0 -resize none
1979
1980    set row 1
1981    set labels { qx qy qz qw xpan ypan zoom }
1982    foreach tag $labels {
1983        label $inner.${tag}label -text $tag -font "Arial 9"
1984        entry $inner.${tag} -font "Arial 9"  -bg white \
1985            -textvariable [itcl::scope _view(-$tag)]
1986        bind $inner.${tag} <Return> \
1987            [itcl::code $this camera set -${tag}]
1988        bind $inner.${tag} <KP_Enter> \
1989            [itcl::code $this camera set -${tag}]
1990        blt::table $inner \
1991            $row,0 $inner.${tag}label -anchor e -pady 2 \
1992            $row,1 $inner.${tag} -anchor w -pady 2
1993        blt::table configure $inner r$row -resize none
1994        incr row
1995    }
1996    checkbutton $inner.ortho \
1997        -text "Orthographic Projection" \
1998        -variable [itcl::scope _view(-ortho)] \
1999        -command [itcl::code $this camera set -ortho] \
2000        -font "Arial 9"
2001    blt::table $inner \
2002            $row,0 $inner.ortho -cspan 2 -anchor w -pady 2
2003    blt::table configure $inner r$row -resize none
2004    incr row
2005
2006    blt::table configure $inner c0 c1 -resize none
2007    blt::table configure $inner c2 -resize expand
2008    blt::table configure $inner r$row -resize expand
2009}
2010
2011itcl::body Rappture::VtkVolumeViewer::BuildCutplaneTab {} {
2012
2013    set fg [option get $itk_component(hull) font Font]
2014   
2015    set inner [$itk_component(main) insert end \
2016        -title "Cutplane Settings" \
2017        -icon [Rappture::icon cutbutton]]
2018
2019    $inner configure -borderwidth 4
2020
2021    checkbutton $inner.visible \
2022        -text "Show Cutplanes" \
2023        -variable [itcl::scope _settings(-cutplanesvisible)] \
2024        -command [itcl::code $this AdjustSetting -cutplanesvisible] \
2025        -font "Arial 9"
2026
2027    checkbutton $inner.lighting \
2028        -text "Enable Lighting" \
2029        -variable [itcl::scope _settings(-cutplanelighting)] \
2030        -command [itcl::code $this AdjustSetting -cutplanelighting] \
2031        -font "Arial 9"
2032
2033    label $inner.opacity_l -text "Opacity" -font "Arial 9"
2034    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
2035        -variable [itcl::scope _settings(-cutplaneopacity)] \
2036        -width 10 \
2037        -showvalue off \
2038        -command [itcl::code $this AdjustSetting -cutplaneopacity]
2039    $inner.opacity set $_settings(-cutplaneopacity)
2040
2041    # X-value slicer...
2042    itk_component add xCutButton {
2043        Rappture::PushButton $inner.xbutton \
2044            -onimage [Rappture::icon x-cutplane] \
2045            -offimage [Rappture::icon x-cutplane] \
2046            -command [itcl::code $this AdjustSetting -xcutplanevisible] \
2047            -variable [itcl::scope _settings(-xcutplanevisible)]
2048    }
2049    Rappture::Tooltip::for $itk_component(xCutButton) \
2050        "Toggle the X-axis cutplane on/off"
2051    $itk_component(xCutButton) select
2052
2053    itk_component add xCutScale {
2054        ::scale $inner.xval -from 100 -to 0 \
2055            -width 10 -orient vertical -showvalue yes \
2056            -borderwidth 1 -highlightthickness 0 \
2057            -command [itcl::code $this EventuallySetCutplane x] \
2058            -variable [itcl::scope _settings(-xcutplaneposition)]
2059    } {
2060        usual
2061        ignore -borderwidth -highlightthickness
2062    }
2063    # Set the default cutplane value before disabling the scale.
2064    $itk_component(xCutScale) set 50
2065    $itk_component(xCutScale) configure -state disabled
2066    Rappture::Tooltip::for $itk_component(xCutScale) \
2067        "@[itcl::code $this Slice tooltip x]"
2068
2069    # Y-value slicer...
2070    itk_component add yCutButton {
2071        Rappture::PushButton $inner.ybutton \
2072            -onimage [Rappture::icon y-cutplane] \
2073            -offimage [Rappture::icon y-cutplane] \
2074            -command [itcl::code $this AdjustSetting -ycutplanevisible] \
2075            -variable [itcl::scope _settings(-ycutplanevisible)]
2076    }
2077    Rappture::Tooltip::for $itk_component(yCutButton) \
2078        "Toggle the Y-axis cutplane on/off"
2079    $itk_component(yCutButton) select
2080
2081    itk_component add yCutScale {
2082        ::scale $inner.yval -from 100 -to 0 \
2083            -width 10 -orient vertical -showvalue yes \
2084            -borderwidth 1 -highlightthickness 0 \
2085            -command [itcl::code $this EventuallySetCutplane y] \
2086            -variable [itcl::scope _settings(-ycutplaneposition)]
2087    } {
2088        usual
2089        ignore -borderwidth -highlightthickness
2090    }
2091    Rappture::Tooltip::for $itk_component(yCutScale) \
2092        "@[itcl::code $this Slice tooltip y]"
2093    # Set the default cutplane value before disabling the scale.
2094    $itk_component(yCutScale) set 50
2095    $itk_component(yCutScale) configure -state disabled
2096
2097    # Z-value slicer...
2098    itk_component add zCutButton {
2099        Rappture::PushButton $inner.zbutton \
2100            -onimage [Rappture::icon z-cutplane] \
2101            -offimage [Rappture::icon z-cutplane] \
2102            -command [itcl::code $this AdjustSetting -zcutplanevisible] \
2103            -variable [itcl::scope _settings(-zcutplanevisible)]
2104    }
2105    Rappture::Tooltip::for $itk_component(zCutButton) \
2106        "Toggle the Z-axis cutplane on/off"
2107    $itk_component(zCutButton) select
2108
2109    itk_component add zCutScale {
2110        ::scale $inner.zval -from 100 -to 0 \
2111            -width 10 -orient vertical -showvalue yes \
2112            -borderwidth 1 -highlightthickness 0 \
2113            -command [itcl::code $this EventuallySetCutplane z] \
2114            -variable [itcl::scope _settings(-zcutplaneposition)]
2115    } {
2116        usual
2117        ignore -borderwidth -highlightthickness
2118    }
2119    $itk_component(zCutScale) set 50
2120    $itk_component(zCutScale) configure -state disabled
2121    Rappture::Tooltip::for $itk_component(zCutScale) \
2122        "@[itcl::code $this Slice tooltip z]"
2123
2124    blt::table $inner \
2125        0,0 $inner.visible              -anchor w -pady 2 -cspan 4 \
2126        1,0 $inner.lighting             -anchor w -pady 2 -cspan 4 \
2127        2,0 $inner.opacity_l            -anchor w -pady 2 -cspan 3 \
2128        3,0 $inner.opacity              -fill x   -pady 2 -cspan 3 \
2129        4,0 $itk_component(xCutButton)  -anchor e -padx 2 -pady 2 \
2130        5,0 $itk_component(xCutScale)   -fill y \
2131        4,1 $itk_component(yCutButton)  -anchor e -padx 2 -pady 2 \
2132        5,1 $itk_component(yCutScale)   -fill y \
2133        4,2 $itk_component(zCutButton)  -anchor e -padx 2 -pady 2 \
2134        5,2 $itk_component(zCutScale)   -fill y
2135
2136    blt::table configure $inner r* c* -resize none
2137    blt::table configure $inner r5 c3 -resize expand
2138}
2139
2140#
2141#  camera --
2142#
2143itcl::body Rappture::VtkVolumeViewer::camera {option args} {
2144    switch -- $option {
2145        "show" {
2146            puts [array get _view]
2147        }
2148        "set" {
2149            set who [lindex $args 0]
2150            set x $_view($who)
2151            set code [catch { string is double $x } result]
2152            if { $code != 0 || !$result } {
2153                return
2154            }
2155            switch -- $who {
2156                "-ortho" {
2157                    if {$_view(-ortho)} {
2158                        SendCmd "camera mode ortho"
2159                    } else {
2160                        SendCmd "camera mode persp"
2161                    }
2162                }
2163                "-xpan" - "-ypan" {
2164                    PanCamera
2165                }
2166                "-qx" - "-qy" - "-qz" - "-qw" {
2167                    set q [ViewToQuaternion]
2168                    $_arcball quaternion $q
2169                    EventuallyRotate $q
2170                }
2171                "-zoom" {
2172                    SendCmd "camera zoom $_view(-zoom)"
2173                }
2174            }
2175        }
2176    }
2177}
2178
2179itcl::body Rappture::VtkVolumeViewer::GetVtkData { args } {
2180    set bytes ""
2181    foreach dataobj [get] {
2182        foreach comp [$dataobj components] {
2183            set tag $dataobj-$comp
2184            set contents [$dataobj vtkdata $comp]
2185            append bytes "$contents\n"
2186        }
2187    }
2188    return [list .vtk $bytes]
2189}
2190
2191itcl::body Rappture::VtkVolumeViewer::GetImage { args } {
2192    if { [image width $_image(download)] > 0 &&
2193         [image height $_image(download)] > 0 } {
2194        set bytes [$_image(download) data -format "jpeg -quality 100"]
2195        set bytes [Rappture::encoding::decode -as b64 $bytes]
2196        return [list .jpg $bytes]
2197    }
2198    return ""
2199}
2200
2201itcl::body Rappture::VtkVolumeViewer::BuildDownloadPopup { popup command } {
2202    Rappture::Balloon $popup \
2203        -title "[Rappture::filexfer::label downloadWord] as..."
2204    set inner [$popup component inner]
2205    label $inner.summary -text "" -anchor w
2206    radiobutton $inner.vtk_button -text "VTK data file" \
2207        -variable [itcl::scope _downloadPopup(format)] \
2208        -font "Helvetica 9 " \
2209        -value vtk 
2210    Rappture::Tooltip::for $inner.vtk_button "Save as VTK data file."
2211    radiobutton $inner.image_button -text "Image File" \
2212        -variable [itcl::scope _downloadPopup(format)] \
2213        -value image
2214    Rappture::Tooltip::for $inner.image_button \
2215        "Save as digital image."
2216
2217    button $inner.ok -text "Save" \
2218        -highlightthickness 0 -pady 2 -padx 3 \
2219        -command $command \
2220        -compound left \
2221        -image [Rappture::icon download]
2222
2223    button $inner.cancel -text "Cancel" \
2224        -highlightthickness 0 -pady 2 -padx 3 \
2225        -command [list $popup deactivate] \
2226        -compound left \
2227        -image [Rappture::icon cancel]
2228
2229    blt::table $inner \
2230        0,0 $inner.summary -cspan 2  \
2231        1,0 $inner.vtk_button -anchor w -cspan 2 -padx { 4 0 } \
2232        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
2233        4,1 $inner.cancel -width .9i -fill y \
2234        4,0 $inner.ok -padx 2 -width .9i -fill y
2235    blt::table configure $inner r3 -height 4
2236    blt::table configure $inner r4 -pady 4
2237    raise $inner.image_button
2238    $inner.vtk_button invoke
2239    return $inner
2240}
2241
2242itcl::body Rappture::VtkVolumeViewer::SetObjectStyle { dataobj cname } {
2243    # Parse style string.
2244    set tag $dataobj-$cname
2245    array set styles {
2246        -color      BCGYR
2247        -lighting   1
2248        -opacity    0.5
2249        -outline    0
2250        -visible    1
2251    }
2252    array set styles [$dataobj style $cname]
2253    set _settings($cname-volumelighting)        $styles(-lighting)
2254    set _settings($cname-volumeopacity)         [expr $styles(-opacity) * 100]
2255    set _settings($cname-volumeoutline)         $styles(-outline)
2256    set _settings($cname-volumevisible)         $styles(-visible)
2257
2258    $itk_component(colormap) value $styles(-color)
2259
2260    SendCmd "outline add $tag"
2261    SendCmd "outline color [Color2RGB $itk_option(-plotforeground)] $tag"
2262    SendCmd "outline visible $styles(-outline) $tag"
2263
2264    SendCmd "$_cutplaneCmd add $tag"
2265    SendCmd "$_cutplaneCmd color [Color2RGB $itk_option(-plotforeground)] $tag"
2266    SendCmd "$_cutplaneCmd visible 0 $tag"
2267
2268    SendCmd "volume add $tag"
2269    SendCmd "volume lighting $styles(-lighting) $tag"
2270    SendCmd "volume opacity $styles(-opacity) $tag"
2271    SendCmd "volume visible $styles(-visible) $tag"
2272
2273    SetInitialTransferFunction $dataobj $cname
2274    SendCmd "volume colormap $cname $tag"
2275    SendCmd "$_cutplaneCmd colormap $cname-opaque $tag"
2276}
2277
2278itcl::body Rappture::VtkVolumeViewer::IsValidObject { dataobj } {
2279    if {[catch {$dataobj isa Rappture::Field} valid] != 0 || !$valid} {
2280        return 0
2281    }
2282    return 1
2283}
2284
2285# ----------------------------------------------------------------------
2286# USAGE: ReceiveLegend <colormap> <title> <vmin> <vmax> <size>
2287#
2288# Invoked automatically whenever the "legend" command comes in from
2289# the rendering server.  Indicates that binary image data with the
2290# specified <size> will follow.
2291# ----------------------------------------------------------------------
2292itcl::body Rappture::VtkVolumeViewer::ReceiveLegend { colormap title vmin vmax size } {
2293    if { [isconnected] } {
2294        set bytes [ReceiveBytes $size]
2295        if { ![info exists _image(legend)] } {
2296            set _image(legend) [image create photo]
2297        }
2298        $_image(legend) configure -data $bytes
2299        #puts stderr "read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
2300        if { [catch {DrawLegend} errs] != 0 } {
2301            puts stderr errs=$errs
2302        }
2303    }
2304}
2305
2306#
2307# DrawLegend --
2308#
2309itcl::body Rappture::VtkVolumeViewer::DrawLegend {} {
2310    if { $_current == "" } {
2311        set _current "component"
2312    }
2313    set cname $_current
2314    set c $itk_component(legend)
2315    set w [winfo width $c]
2316    set h [winfo height $c]
2317    set lx 10
2318    set ly [expr {$h - 1}]
2319    if {"" == [$c find withtag colorbar]} {
2320        $c create image 10 10 -anchor nw \
2321            -image $_image(legend) -tags colorbar
2322        $c create text $lx $ly -anchor sw \
2323            -fill $itk_option(-plotforeground) -tags "limits text vmin"
2324        $c create text [expr {$w-$lx}] $ly -anchor se \
2325            -fill $itk_option(-plotforeground) -tags "limits text vmax"
2326        $c create text [expr {$w/2}] $ly -anchor s \
2327            -fill $itk_option(-plotforeground) -tags "limits text title"
2328        $c lower colorbar
2329        $c bind colorbar <ButtonRelease-1> [itcl::code $this AddNewMarker %x %y]
2330    }
2331
2332    # Display the markers used by the current transfer function.
2333    HideAllMarkers
2334    if { [info exists _transferFunctionEditors($cname)] } {
2335        $_transferFunctionEditors($cname) showMarkers $_limits($cname)
2336    }
2337
2338    foreach {min max} $_limits($cname) break
2339    $c itemconfigure vmin -text [format %g $min]
2340    $c coords vmin $lx $ly
2341
2342    $c itemconfigure vmax -text [format %g $max]
2343    $c coords vmax [expr {$w-$lx}] $ly
2344
2345    set title ""
2346    if { $_first != "" } {
2347        set title [$_first hints label]
2348        set units [$_first hints units]
2349        if { $units != "" } {
2350            set title "$title ($units)"
2351        }
2352    }
2353    $c itemconfigure title -text $title
2354    $c coords title [expr {$w/2}] $ly
2355}
2356
2357#
2358# DrawLegendOld --
2359#
2360#       Draws the legend in it's own canvas which resides to the right
2361#       of the contour plot area.
2362#
2363itcl::body Rappture::VtkVolumeViewer::DrawLegendOld { } {
2364    set fname $_curFldName
2365    set c $itk_component(view)
2366    set w [winfo width $c]
2367    set h [winfo height $c]
2368    set font "Arial 8"
2369    set lineht [font metrics $font -linespace]
2370   
2371    if { [info exists _fields($fname)] } {
2372        foreach { title units } $_fields($fname) break
2373        if { $units != "" } {
2374            set title [format "%s (%s)" $title $units]
2375        }
2376    } else {
2377        set title $fname
2378    }
2379    if { $_settings(-legendvisible) } {
2380        set x [expr $w - 2]
2381        if { [$c find withtag "legend"] == "" } {
2382            set y 2
2383            $c create text $x $y \
2384                -anchor ne \
2385                -fill $itk_option(-plotforeground) -tags "title legend" \
2386                -font $font
2387            incr y $lineht
2388            $c create text $x $y \
2389                -anchor ne \
2390                -fill $itk_option(-plotforeground) -tags "vmax legend" \
2391                -font $font
2392            incr y $lineht
2393            $c create image $x $y \
2394                -anchor ne \
2395                -image $_image(legend) -tags "colormap legend"
2396            $c create text $x [expr {$h-2}] \
2397                -anchor se \
2398                -fill $itk_option(-plotforeground) -tags "vmin legend" \
2399                -font $font
2400            #$c bind colormap <Enter> [itcl::code $this EnterLegend %x %y]
2401            $c bind colormap <Leave> [itcl::code $this LeaveLegend]
2402            $c bind colormap <Motion> [itcl::code $this MotionLegend %x %y]
2403        }
2404        $c bind title <ButtonPress> [itcl::code $this Combo post]
2405        $c bind title <Enter> [itcl::code $this Combo activate]
2406        $c bind title <Leave> [itcl::code $this Combo deactivate]
2407        # Reset the item coordinates according the current size of the plot.
2408        $c itemconfigure title -text $title
2409        if { [info exists _limits($_curFldName)] } {
2410            foreach { vmin vmax } $_limits($_curFldName) break
2411            $c itemconfigure vmin -text [format %g $vmin]
2412            $c itemconfigure vmax -text [format %g $vmax]
2413        }
2414        set y 2
2415        $c coords title $x $y
2416        incr y $lineht
2417        $c coords vmax $x $y
2418        incr y $lineht
2419        $c coords colormap $x $y
2420        $c coords vmin $x [expr {$h - 2}]
2421    }
2422}
2423
2424#
2425# EnterLegend --
2426#
2427itcl::body Rappture::VtkVolumeViewer::EnterLegend { x y } {
2428    SetLegendTip $x $y
2429}
2430
2431#
2432# MotionLegend --
2433#
2434itcl::body Rappture::VtkVolumeViewer::MotionLegend { x y } {
2435    Rappture::Tooltip::tooltip cancel
2436    set c $itk_component(view)
2437    SetLegendTip $x $y
2438}
2439
2440#
2441# LeaveLegend --
2442#
2443itcl::body Rappture::VtkVolumeViewer::LeaveLegend { } {
2444    Rappture::Tooltip::tooltip cancel
2445    .rappturetooltip configure -icon ""
2446}
2447
2448#
2449# SetLegendTip --
2450#
2451itcl::body Rappture::VtkVolumeViewer::SetLegendTip { x y } {
2452    set c $itk_component(view)
2453    set w [winfo width $c]
2454    set h [winfo height $c]
2455    set font "Arial 8"
2456    set lineht [font metrics $font -linespace]
2457   
2458    set imgHeight [image height $_image(legend)]
2459    set coords [$c coords colormap]
2460    set imgX [expr $w - [image width $_image(legend)] - 2]
2461    set imgY [expr $y - 2 * ($lineht + 2)]
2462
2463    if { [info exists _fields($_title)] } {
2464        foreach { title units } $_fields($_title) break
2465        if { $units != "" } {
2466            set title [format "%s (%s)" $title $units]
2467        }
2468    } else {
2469        set title $_title
2470    }
2471    # Make a swatch of the selected color
2472    if { [catch { $_image(legend) get 10 $imgY } pixel] != 0 } {
2473        #puts stderr "out of range: $imgY"
2474        return
2475    }
2476    if { ![info exists _image(swatch)] } {
2477        set _image(swatch) [image create photo -width 24 -height 24]
2478    }
2479    set color [eval format "\#%02x%02x%02x" $pixel]
2480    $_image(swatch) put black  -to 0 0 23 23
2481    $_image(swatch) put $color -to 1 1 22 22
2482    .rappturetooltip configure -icon $_image(swatch)
2483
2484    # Compute the value of the point
2485    if { [info exists _limits($_curFldName)] } {
2486        foreach { vmin vmax } $_limits($_curFldName) break
2487        set t [expr 1.0 - (double($imgY) / double($imgHeight-1))]
2488        set value [expr $t * ($vmax - $vmin) + $vmin]
2489    } else {
2490        set value 0.0
2491    }
2492    set tipx [expr $x + 15]
2493    set tipy [expr $y - 5]
2494    Rappture::Tooltip::text $c "$title $value"
2495    Rappture::Tooltip::tooltip show $c +$tipx,+$tipy   
2496}
2497
2498# ----------------------------------------------------------------------
2499# USAGE: Slice move x|y|z <newval>
2500#
2501# Called automatically when the user drags the slider to move the
2502# cut plane that slices 3D data.  Gets the current value from the
2503# slider and moves the cut plane to the appropriate point in the
2504# data set.
2505# ----------------------------------------------------------------------
2506itcl::body Rappture::VtkVolumeViewer::Slice {option args} {
2507    switch -- $option {
2508        "move" {
2509            set axis [lindex $args 0]
2510            set newval [lindex $args 1]
2511            if {[llength $args] != 2} {
2512                error "wrong # args: should be \"Slice move x|y|z newval\""
2513            }
2514            set newpos [expr {0.01*$newval}]
2515            SendCmd "$_cutplaneCmd slice $axis $newpos"
2516        }
2517        "tooltip" {
2518            set axis [lindex $args 0]
2519            set val [$itk_component(${axis}CutScale) get]
2520            return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
2521        }
2522        default {
2523            error "bad option \"$option\": should be axis, move, or tooltip"
2524        }
2525    }
2526}
2527
2528# ----------------------------------------------------------------------
2529# USAGE: _dropdown post
2530# USAGE: _dropdown unpost
2531# USAGE: _dropdown select
2532#
2533# Used internally to handle the dropdown list for this combobox.  The
2534# post/unpost options are invoked when the list is posted or unposted
2535# to manage the relief of the controlling button.  The select option
2536# is invoked whenever there is a selection from the list, to assign
2537# the value back to the gauge.
2538# ----------------------------------------------------------------------
2539itcl::body Rappture::VtkVolumeViewer::Combo {option} {
2540    set c $itk_component(view)
2541    switch -- $option {
2542        post {
2543            foreach { x1 y1 x2 y2 } [$c bbox title] break
2544            set x1 [expr [winfo width $itk_component(view)] - [winfo reqwidth $itk_component(fieldmenu)]]
2545            set x [expr $x1 + [winfo rootx $itk_component(view)]]
2546            set y [expr $y2 + [winfo rooty $itk_component(view)]]
2547            tk_popup $itk_component(fieldmenu) $x $y
2548        }
2549        activate {
2550            $c itemconfigure title -fill red
2551        }
2552        deactivate {
2553            $c itemconfigure title -fill $itk_option(-plotforeground)
2554        }
2555        invoke {
2556            $itk_component(field) value $_curFldLabel
2557            AdjustSetting -field
2558        }
2559        default {
2560            error "bad option \"$option\": should be post, unpost, select"
2561        }
2562    }
2563}
2564
2565#
2566# The -levels option takes a single value that represents the number
2567# of evenly distributed markers based on the current data range. Each
2568# marker is a relative value from 0.0 to 1.0.
2569#
2570itcl::body Rappture::VtkVolumeViewer::ParseLevelsOption { cname levels } {
2571    set c $itk_component(legend)
2572    set list {}
2573    regsub -all "," $levels " " levels
2574    if {[string is int $levels]} {
2575        for {set i 1} { $i <= $levels } {incr i} {
2576            lappend list [expr {double($i)/($levels+1)}]
2577        }
2578    } else {
2579        foreach x $levels {
2580            lappend list $x
2581        }
2582    }
2583    set _parsedFunction($cname) 1
2584    $_transferFunctionEditors($cname) addMarkers $list
2585}
2586
2587#
2588# The -markers option takes a list of zero or more values (the values
2589# may be separated either by spaces or commas) that have the following
2590# format:
2591#
2592#   N%  Percent of current total data range.  Converted to
2593#       to a relative value between 0.0 and 1.0.
2594#   N   Absolute value of marker.  If the marker is outside of
2595#       the current range, it will be displayed on the outer
2596#       edge of the legends, but it range it represents will
2597#       not be seen.
2598#
2599itcl::body Rappture::VtkVolumeViewer::ParseMarkersOption { cname markers } {
2600    set c $itk_component(legend)
2601    set list {}
2602    foreach { min max } $_limits($cname) break
2603    regsub -all "," $markers " " markers
2604    foreach marker $markers {
2605        set n [scan $marker "%g%s" value suffix]
2606        if { $n == 2 && $suffix == "%" } {
2607            # $n% : Set relative value (0..1).
2608            lappend list [expr {$value * 0.01}]
2609        } else {
2610            # $n : absolute value, compute relative
2611            lappend list  [expr {(double($value)-$min)/($max-$min)]}
2612        }
2613    }
2614    set _parsedFunction($cname) 1
2615    $_transferFunctionEditors($cname) addMarkers $list
2616}
2617
2618#
2619# SetInitialTransferFunction --
2620#
2621#       Creates a transfer function name based on the <style> settings in the
2622#       library run.xml file. This placeholder will be used later to create
2623#       and send the actual transfer function once the data info has been sent
2624#       to us by the render server. [We won't know the volume limits until the
2625#       server parses the 3D data and sends back the limits via ReceiveData.]
2626#
2627itcl::body Rappture::VtkVolumeViewer::SetInitialTransferFunction { dataobj cname } {
2628    set tag $dataobj-$cname
2629    if { ![info exists _cname2transferFunction($cname)] } {
2630        ComputeTransferFunction $cname
2631    }
2632    set _dataset2style($tag) $cname
2633    lappend _style2datasets($cname) $tag
2634
2635    return $cname
2636}
2637
2638#
2639# ComputeTransferFunction --
2640#
2641#       Computes and sends the transfer function to the render server.  It's
2642#       assumed that the volume data limits are known and that the global
2643#       transfer-functions slider values have been set up.  Both parts are
2644#       needed to compute the relative value (location) of the marker, and
2645#       the alpha map of the transfer function.
2646#
2647itcl::body Rappture::VtkVolumeViewer::ComputeTransferFunction { cname } {
2648
2649    if { ![info exists _transferFunctionEditors($cname)] } {
2650        set _transferFunctionEditors($cname) \
2651            [Rappture::TransferFunctionEditor ::\#auto $itk_component(legend) \
2652                 $cname \
2653                 -command [itcl::code $this updateTransferFunctions]]
2654    }
2655
2656    # We have to parse the style attributes for a volume using this
2657    # transfer-function *once*.  This sets up the initial isomarkers for the
2658    # transfer function.  The user may add/delete markers, so we have to
2659    # maintain a list of markers for each transfer-function.  We use the one
2660    # of the volumes (the first in the list) using the transfer-function as a
2661    # reference.
2662
2663    if { ![info exists _parsedFunction($cname)] || ![info exists _cname2transferFunction($cname)] } {
2664        array set styles {
2665            -color BCGYR
2666            -alphamap ""
2667            -levels 6
2668            -markers ""
2669        }
2670        # Accumulate the style from all the datasets using it.
2671        foreach tag [GetDatasetsWithComponent $cname] {
2672            foreach {dataobj cname} [split [lindex $tag 0] -] break
2673            set option [lindex [$dataobj components -style $cname] 0]
2674            array set styles $option
2675        }
2676        set _settings($cname-color) $styles(-color)
2677        set cmap [ColorsToColormap $styles(-color)]
2678        set _cname2defaultcolormap($cname) $cmap
2679        if { [info exists _transferFunctionEditors($cname)] } {
2680            eval $_transferFunctionEditors($cname) limits $_limits($cname)
2681        }
2682        if { [info exists styles(-markers)] &&
2683             [llength $styles(-markers)] > 0 } {
2684            ParseMarkersOption $cname $styles(-markers)
2685        } else {
2686            ParseLevelsOption $cname $styles(-levels)
2687        }
2688        if { $styles(-alphamap) != "" } {
2689            set _alphamap($cname) $styles(-alphamap)
2690        }
2691    } else {
2692        foreach {cmap amap} $_cname2transferFunction($cname) break
2693    }
2694    if { ![info exists _alphamap($cname)] } {
2695        set amap [ComputeAlphamap $cname]
2696    } else {
2697        set amap $_alphamap($cname)
2698    }
2699    set opaqueAmap "0.0 1.0 1.0 1.0"
2700    set _cname2transferFunction($cname) [list $cmap $amap]
2701    SendCmd [list colormap define $cname $cmap $amap]
2702    SendCmd [list colormap define $cname-opaque $cmap $opaqueAmap]
2703}
2704
2705#
2706# ResetColormap --
2707#
2708#       Changes only the colormap portion of the transfer function.
2709#
2710itcl::body Rappture::VtkVolumeViewer::ResetColormap { cname color } {
2711    # Get the current transfer function
2712    if { ![info exists _cname2transferFunction($cname)] } {
2713        return
2714    }
2715    foreach { cmap amap } $_cname2transferFunction($cname) break
2716    set cmap [GetColormap $cname $color]
2717    set _cname2transferFunction($cname) [list $cmap $amap]
2718    set opaqueAmap "0.0 1.0 1.0 1.0"
2719    SendCmd [list colormap define $cname $cmap $amap]
2720    SendCmd [list colormap define $cname-opaque $cmap $opaqueAmap]
2721    EventuallyRequestLegend
2722}
2723
2724# ----------------------------------------------------------------------
2725# USAGE: updateTransferFunctions
2726#
2727#       This is called by the transfer function editor whenever the
2728#       transfer function definition changes.
2729#
2730# ----------------------------------------------------------------------
2731itcl::body Rappture::VtkVolumeViewer::updateTransferFunctions {} {
2732    foreach cname [array names _volcomponents] {
2733        ComputeTransferFunction $cname
2734    }
2735    EventuallyRequestLegend
2736}
2737
2738itcl::body Rappture::VtkVolumeViewer::AddNewMarker { x y } {
2739    if { ![info exists _transferFunctionEditors($_current)] } {
2740        continue
2741    }
2742    # Add a new marker to the current transfer function
2743    $_transferFunctionEditors($_current) newMarker $x $y normal
2744}
2745
2746itcl::body Rappture::VtkVolumeViewer::RemoveMarker { x y } {
2747    if { ![info exists _transferFunctionEditors($_current)] } {
2748        continue
2749    }
2750    # Add a new marker to the current transfer function
2751    $_transferFunctionEditors($_current) deleteMarker $x $y
2752}
2753
2754itcl::body Rappture::VtkVolumeViewer::SetOrientation { side } {
2755    array set positions {
2756        front "1 0 0 0"
2757        back  "0 0 1 0"
2758        left  "0.707107 0 -0.707107 0"
2759        right "0.707107 0 0.707107 0"
2760        top   "0.707107 -0.707107 0 0"
2761        bottom "0.707107 0.707107 0 0"
2762    }
2763    foreach name { -qw -qx -qy -qz } value $positions($side) {
2764        set _view($name) $value
2765    }
2766    set q [ViewToQuaternion]
2767    $_arcball quaternion $q
2768    SendCmd "camera orient $q"
2769    SendCmd "camera reset"
2770    set _view(-xpan) 0
2771    set _view(-ypan) 0
2772    set _view(-zoom) 1.0
2773}
2774
2775#
2776# InitComponentSettings --
2777#
2778#       Initializes the volume settings for a specific component. This
2779#       should match what's used as global settings above. This
2780#       is called the first time we try to switch to a given component
2781#       in SwitchComponent below.
2782#
2783itcl::body Rappture::VtkVolumeViewer::InitComponentSettings { cname } {
2784    array set _settings [subst {
2785        $cname-color                    default
2786        $cname-volumeambient            40
2787        $cname-volumeblendmode          composite
2788        $cname-volumediffuse            60
2789        $cname-volumelight2side         1
2790        $cname-volumelighting           1
2791        $cname-volumeopacity            50
2792        $cname-volumeoutline            0
2793        $cname-volumequality            80
2794        $cname-volumespecularexponent   90
2795        $cname-volumespecularlevel      30
2796        $cname-volumethickness          350
2797        $cname-volumevisible            1
2798    }]
2799}
2800
2801#
2802# SwitchComponent --
2803#
2804#       This is called when the current component is changed by the
2805#       dropdown menu in the volume tab.  It synchronizes the global
2806#       volume settings with the settings of the new current component.
2807#
2808itcl::body Rappture::VtkVolumeViewer::SwitchComponent { cname } {
2809    if { ![info exists _settings(${cname}-volumeambient)] } {
2810        InitComponentSettings $cname
2811    }
2812    # _settings variables change widgets, except for colormap
2813    foreach name {
2814        -volumeambient
2815        -volumeblendmode
2816        -volumediffuse
2817        -volumelight2side
2818        -volumelighting
2819        -volumeopacity
2820        -volumeoutline
2821        -volumequality
2822        -volumespecularexponent
2823        -volumespecularlevel
2824        -volumethickness
2825        -volumevisible
2826    } {
2827        set _settings($name) $_settings(${cname}${name})
2828    }
2829    $itk_component(colormap) value        $_settings($cname-color)
2830    set _current $cname;                # Reset the current component
2831}
2832
2833itcl::body Rappture::VtkVolumeViewer::ComputeAlphamap { cname } {
2834    if { ![info exists _transferFunctionEditors($cname)] } {
2835        return [list 0.0 0.0 1.0 1.0]
2836    }
2837    if { ![info exists _settings($cname-volumeambient)] } {
2838        InitComponentSettings $cname
2839    }
2840
2841    set isovalues [$_transferFunctionEditors($cname) values]
2842
2843    # Currently using volume opacity to scale opacity in
2844    # the volume shader. The transfer function always sets full
2845    # opacity
2846    set max 1.0;
2847
2848    # Use the component-wise thickness setting from the slider
2849    # settings widget
2850    # Scale values between 0.00001 and 0.01000
2851    set delta [expr {double($_settings($cname-volumethickness)) * 0.0001}]
2852    set first [lindex $isovalues 0]
2853    set last [lindex $isovalues end]
2854    set amap ""
2855    if { $first == "" || $first != 0.0 } {
2856        lappend amap 0.0 0.0
2857    }
2858    foreach x $isovalues {
2859        set x1 [expr {$x-$delta-0.00001}]
2860        set x2 [expr {$x-$delta}]
2861        set x3 [expr {$x+$delta}]
2862        set x4 [expr {$x+$delta+0.00001}]
2863        if { $x1 < 0.0 } {
2864            set x1 0.0
2865        } elseif { $x1 > 1.0 } {
2866            set x1 1.0
2867        }
2868        if { $x2 < 0.0 } {
2869            set x2 0.0
2870        } elseif { $x2 > 1.0 } {
2871            set x2 1.0
2872        }
2873        if { $x3 < 0.0 } {
2874            set x3 0.0
2875        } elseif { $x3 > 1.0 } {
2876            set x3 1.0
2877        }
2878        if { $x4 < 0.0 } {
2879            set x4 0.0
2880        } elseif { $x4 > 1.0 } {
2881            set x4 1.0
2882        }
2883        # add spikes in the middle
2884        lappend amap $x1 0.0
2885        lappend amap $x2 $max
2886        lappend amap $x3 $max
2887        lappend amap $x4 0.0
2888    }
2889    if { $last == "" || $last != 1.0 } {
2890        lappend amap 1.0 0.0
2891    }
2892    return $amap
2893}
2894
2895#
2896# HideAllMarkers --
2897#
2898#       Hide all the markers in all the transfer functions.  Can't simply
2899#       delete and recreate markers from the <style> since the user may
2900#       have create, deleted, or moved markers.
2901#
2902itcl::body Rappture::VtkVolumeViewer::HideAllMarkers {} {
2903    foreach cname [array names _transferFunctionEditors] {
2904        $_transferFunctionEditors($cname) hideMarkers
2905    }
2906}
2907
2908#
2909# GetDatasetsWithComponents --
2910#
2911#       Returns a list of all the datasets (known by the combination of
2912#       their data object and component name) that match the given
2913#       component name.  For example, this is used where we want to change
2914#       the settings of volumes that have the current component.
2915#
2916itcl::body Rappture::VtkVolumeViewer::GetDatasetsWithComponent { cname } {
2917    if { ![info exists _volcomponents($cname)] } {
2918        return ""
2919    }
2920    return $_volcomponents($cname)
2921}
2922
2923#
2924# BuildVolumeComponents --
2925#
2926#       This is called from the "scale" method which is called when a
2927#       new dataset is added or deleted.  It repopulates the dropdown
2928#       menu of volume component names.  It sets the current component
2929#       to the first component in the list (of components found).
2930#       Finally, if there is only one component, don't display the
2931#       label or the combobox in the volume settings tab.
2932#
2933itcl::body Rappture::VtkVolumeViewer::BuildVolumeComponents {} {
2934    $itk_component(volcomponents) choices delete 0 end
2935    foreach name $_componentsList {
2936        $itk_component(volcomponents) choices insert end $name $name
2937    }
2938    set _current [lindex $_componentsList 0]
2939    $itk_component(volcomponents) value $_current
2940    set parent [winfo parent $itk_component(volcomponents)]
2941    if { [llength $_componentsList] <= 1 } {
2942        # Unpack the components label and dropdown if there's only one
2943        # component.
2944        blt::table forget $parent.volcomponents_l $parent.volcomponents
2945    } else {
2946        # Pack the components label and dropdown into the table there's
2947        # more than one component to select.
2948        blt::table $parent \
2949            0,0 $parent.volcomponents_l -anchor e -cspan 2 \
2950            0,2 $parent.volcomponents -cspan 3 -fill x
2951    }
2952}
2953
2954itcl::body Rappture::VtkVolumeViewer::GetColormap { cname color } {
2955    if { $color == "default" } {
2956        return $_cname2defaultcolormap($cname)
2957    }
2958    return [ColorsToColormap $color]
2959}
Note: See TracBrowser for help on using the repository browser.