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

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

Fix for vtkvolume tester: prevent infinite loop

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