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

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

fix flowvisviewer for limits method in field, refactored isomarkers into transferfunctioneditor for nanovisviewer

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