source: branches/1.3/gui/scripts/vtkvolumeviewer.tcl @ 4748

Last change on this file since 4748 was 4748, checked in by ldelgass, 7 years ago

merge r4747 from trunk

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