source: branches/1.3/gui/scripts/flowvisviewer.tcl @ 3844

Last change on this file since 3844 was 3844, checked in by ldelgass, 11 years ago

Sync with trunk. Branch now differs only from trunk by r3722 (branch is version
1.3, trunk is version 1.4)

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