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

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