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

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

fix up stats reporting in servers

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