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

Last change on this file since 3477 was 3477, checked in by ldelgass, 12 years ago

Add debug console binding to flowvisviewer

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