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

Last change on this file since 1545 was 1545, checked in by gah, 15 years ago

add transparency to heightmap

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