source: trunk/gui/scripts/flowvisviewer.tcl @ 5092

Last change on this file since 5092 was 5092, checked in by ldelgass, 5 years ago

Add update in Rebuild to allow initial canvas size to be set. We don't want to
do this in the constructor since that can cause an error when the add method is
called before the constructor returns.

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