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

Last change on this file since 3984 was 3984, checked in by gah, 11 years ago

fix for new markers (immediately send new transfer function, don't create new marker is close to an existing marker).

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