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

Last change on this file since 4165 was 4165, checked in by ldelgass, 10 years ago

Fixes for legend background color in flowvisviewer. Still has bugs with
transfer function editor and legend label color.

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