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

Last change on this file since 5278 was 5278, checked in by ldelgass, 6 years ago

merge r5276 from trunk

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