source: branches/1.4/gui/scripts/flowvisviewer.tcl @ 5655

Last change on this file since 5655 was 5655, checked in by ldelgass, 9 years ago

merge from trunk

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