source: trunk/gui/scripts/vtkstreamlinesviewer.tcl @ 3371

Last change on this file since 3371 was 3371, checked in by gah, 12 years ago

Move ColorsToColormap? into base VisViewer? class

File size: 90.1 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: vtkviewer - Vtk drawing object viewer
4#
5#  It connects to the Vtk server running on a rendering farm,
6#  transmits data, and displays the results.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13# ======================================================================
14package require Itk
15package require BLT
16package require Img
17
18option add *VtkStreamlinesViewer.width 4i widgetDefault
19option add *VtkStreamlinesViewer*cursor crosshair widgetDefault
20option add *VtkStreamlinesViewer.height 4i widgetDefault
21option add *VtkStreamlinesViewer.foreground black widgetDefault
22option add *VtkStreamlinesViewer.controlBackground gray widgetDefault
23option add *VtkStreamlinesViewer.controlDarkBackground #999999 widgetDefault
24option add *VtkStreamlinesViewer.plotBackground black widgetDefault
25option add *VtkStreamlinesViewer.plotForeground white widgetDefault
26option add *VtkStreamlinesViewer.font \
27    -*-helvetica-medium-r-normal-*-12-* widgetDefault
28
29# must use this name -- plugs into Rappture::resources::load
30proc VtkStreamlinesViewer_init_resources {} {
31    Rappture::resources::register \
32        vtkvis_server Rappture::VtkStreamlinesViewer::SetServerList
33}
34
35itcl::class Rappture::VtkStreamlinesViewer {
36    inherit Rappture::VisViewer
37
38    itk_option define -plotforeground plotForeground Foreground ""
39    itk_option define -plotbackground plotBackground Background ""
40
41    constructor { hostlist args } {
42        Rappture::VisViewer::constructor $hostlist
43    } {
44        # defined below
45    }
46    destructor {
47        # defined below
48    }
49    public proc SetServerList { namelist } {
50        Rappture::VisViewer::SetServerList "vtkvis" $namelist
51    }
52    public method add {dataobj {settings ""}}
53    public method camera {option args}
54    public method delete {args}
55    public method disconnect {}
56    public method download {option args}
57    public method get {args}
58    public method isconnected {}
59    public method limits { colormap }
60    public method sendto { string }
61    public method parameters {title args} {
62        # do nothing
63    }
64    public method scale {args}
65
66    protected method Connect {}
67    protected method CurrentDatasets {args}
68    protected method Disconnect {}
69    protected method DoResize {}
70    protected method DoReseed {}
71    protected method DoRotate {}
72    protected method AdjustSetting {what {value ""}}
73    protected method InitSettings { args  }
74    protected method Pan {option x y}
75    protected method Pick {x y}
76    protected method Rebuild {}
77    protected method ReceiveDataset { args }
78    protected method ReceiveImage { args }
79    protected method ReceiveLegend { colormap title vmin vmax size }
80    protected method Rotate {option x y}
81    protected method SendCmd {string}
82    protected method SendCmdNoSplash {string}
83    protected method Zoom {option}
84
85    # The following methods are only used by this class.
86    private method BuildAxisTab {}
87    private method BuildCameraTab {}
88    private method BuildColormap { name colors }
89    private method BuildCutplaneTab {}
90    private method BuildDownloadPopup { widget command }
91    private method BuildStreamsTab {}
92    private method BuildVolumeTab {}
93    private method ConvertToVtkData { dataobj comp }
94    private method DrawLegend { title }
95    private method Combo { option }
96    private method EnterLegend { x y }
97    private method EventuallyResize { w h }
98    private method EventuallyReseed { numPoints }
99    private method EventuallyRotate { q }
100    private method EventuallySetCutplane { axis args }
101    private method GetImage { args }
102    private method GetVtkData { args }
103    private method IsValidObject { dataobj }
104    private method LeaveLegend {}
105    private method MotionLegend { x y }
106    private method PanCamera {}
107    private method RequestLegend {}
108    private method SetColormap { dataobj comp }
109    private method ChangeColormap { dataobj comp color }
110    private method SetLegendTip { x y }
111    private method SetObjectStyle { dataobj comp }
112    private method Slice {option args}
113
114    private variable _arcball ""
115    private variable _outbuf       ;    # buffer for outgoing commands
116
117    private variable _dlist ""     ;    # list of data objects
118    private variable _obj2datasets
119    private variable _obj2ovride   ;    # maps dataobj => style override
120    private variable _datasets     ;    # contains all the dataobj-component
121                                   ;    # datasets in the server
122    private variable _colormaps    ;    # contains all the colormaps
123                                   ;    # in the server.
124    private variable _dataset2style    ;# maps dataobj-component to transfunc
125
126    private variable _click        ;    # info used for rotate operations
127    private variable _limits       ;    # autoscale min/max for all axes
128    private variable _view         ;    # view params for 3D view
129    private variable _settings
130    private variable _style;            # Array of current component styles.
131    private variable _initialStyle;     # Array of initial component styles.
132    private variable _reset 1;          # indicates if camera needs to be reset
133                                        # to starting position.
134
135    private variable _first ""     ;    # This is the topmost dataset.
136    private variable _start 0
137    private variable _buffering 0
138    private variable _title ""
139    private variable _seeds
140
141    common _downloadPopup;              # download options from popup
142    private common _hardcopy
143    private variable _width 0
144    private variable _height 0
145    private variable _resizePending 0
146    private variable _reseedPending 0
147    private variable _rotatePending 0
148    private variable _cutplanePending 0
149    private variable _legendPending 0
150    private variable _outline
151    private variable _vectorFields
152    private variable _scalarFields
153    private variable _fields
154    private variable _currentField ""
155    private variable _field      ""
156    private variable _numSeeds 200
157    private variable _colorMode "vmag";#  Mode of colormap (vmag or scalar)
158}
159
160itk::usual VtkStreamlinesViewer {
161    keep -background -foreground -cursor -font
162    keep -plotbackground -plotforeground
163}
164
165# ----------------------------------------------------------------------
166# CONSTRUCTOR
167# ----------------------------------------------------------------------
168itcl::body Rappture::VtkStreamlinesViewer::constructor {hostlist args} {
169    package require vtk
170    set _serverType "vtkvis"
171
172    # Rebuild event
173    $_dispatcher register !rebuild
174    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
175
176    # Resize event
177    $_dispatcher register !resize
178    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
179
180    # Reseed event
181    $_dispatcher register !reseed
182    $_dispatcher dispatch $this !reseed "[itcl::code $this DoReseed]; list"
183
184    # Rotate event
185    $_dispatcher register !rotate
186    $_dispatcher dispatch $this !rotate "[itcl::code $this DoRotate]; list"
187
188    # Legend event
189    $_dispatcher register !legend
190    $_dispatcher dispatch $this !legend "[itcl::code $this RequestLegend]; list"
191
192    # X-Cutplane event
193    $_dispatcher register !xcutplane
194    $_dispatcher dispatch $this !xcutplane \
195        "[itcl::code $this AdjustSetting cutplaneXPosition]; list"
196
197    # Y-Cutplane event
198    $_dispatcher register !ycutplane
199    $_dispatcher dispatch $this !ycutplane \
200        "[itcl::code $this AdjustSetting cutplaneYPosition]; list"
201
202    # Z-Cutplane event
203    $_dispatcher register !zcutplane
204    $_dispatcher dispatch $this !zcutplane \
205        "[itcl::code $this AdjustSetting cutplaneZPosition]; list"
206
207    #
208    # Populate parser with commands handle incoming requests
209    #
210    $_parser alias image [itcl::code $this ReceiveImage]
211    $_parser alias dataset [itcl::code $this ReceiveDataset]
212    $_parser alias legend [itcl::code $this ReceiveLegend]
213
214    array set _outline {
215        id -1
216        afterId -1
217        x1 -1
218        y1 -1
219        x2 -1
220        y2 -1
221    }
222    # Initialize the view to some default parameters.
223    array set _view {
224        qw              0.853553
225        qx              -0.353553
226        qy              0.353553
227        qz              0.146447
228        zoom            1.0
229        xpan            0
230        ypan            0
231        ortho           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(zmin) 0.0
238    set _limits(zmax) 1.0
239
240    array set _settings [subst {
241        axesVisible             1
242        axisLabelsVisible       1
243        axisXGrid               0
244        axisYGrid               0
245        axisZGrid               0
246        cutplaneEdges           0
247        cutplaneLighting        1
248        cutplaneOpacity         100
249        cutplaneVisible         0
250        cutplaneWireframe       0
251        cutplaneXPosition       50
252        cutplaneXVisible        1
253        cutplaneYPosition       50
254        cutplaneYVisible        1
255        cutplaneZPosition       50
256        cutplaneZVisible        1
257        legendVisible           1
258        streamlinesLighting     1
259        streamlinesMode         lines
260        streamlinesNumSeeds     200
261        streamlinesOpacity      100
262        streamlinesScale        1
263        streamlinesSeedsVisible 0
264        streamlinesVisible      1
265        volumeEdges             0
266        volumeLighting          1
267        volumeOpacity           40
268        volumeVisible           1
269        volumeWireframe         0
270    }]
271
272    itk_component add view {
273        canvas $itk_component(plotarea).view \
274            -highlightthickness 0 -borderwidth 0
275    } {
276        usual
277        ignore -highlightthickness -borderwidth  -background
278    }
279
280    itk_component add fieldmenu {
281        menu $itk_component(plotarea).menu -bg black -fg white -relief flat \
282            -tearoff no
283    } {
284        usual
285        ignore -background -foreground -relief -tearoff
286    }
287    set c $itk_component(view)
288    bind $c <Configure> [itcl::code $this EventuallyResize %w %h]
289    bind $c <4> [itcl::code $this Zoom in 0.25]
290    bind $c <5> [itcl::code $this Zoom out 0.25]
291    bind $c <KeyPress-Left>  [list %W xview scroll 10 units]
292    bind $c <KeyPress-Right> [list %W xview scroll -10 units]
293    bind $c <KeyPress-Up>    [list %W yview scroll 10 units]
294    bind $c <KeyPress-Down>  [list %W yview scroll -10 units]
295    bind $c <Enter> "focus %W"
296    bind $c <Control-F1> [itcl::code $this ToggleConsole]
297
298    # Fix the scrollregion in case we go off screen
299    $c configure -scrollregion [$c bbox all]
300
301    set _map(id) [$c create image 0 0 -anchor nw -image $_image(plot)]
302    set _map(cwidth) -1
303    set _map(cheight) -1
304    set _map(zoom) 1.0
305    set _map(original) ""
306
307    set f [$itk_component(main) component controls]
308    itk_component add reset {
309        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
310            -highlightthickness 0 \
311            -image [Rappture::icon reset-view] \
312            -command [itcl::code $this Zoom reset]
313    } {
314        usual
315        ignore -highlightthickness
316    }
317    pack $itk_component(reset) -side top -padx 2 -pady 2
318    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
319
320    itk_component add zoomin {
321        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
322            -highlightthickness 0 \
323            -image [Rappture::icon zoom-in] \
324            -command [itcl::code $this Zoom in]
325    } {
326        usual
327        ignore -highlightthickness
328    }
329    pack $itk_component(zoomin) -side top -padx 2 -pady 2
330    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
331
332    itk_component add zoomout {
333        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
334            -highlightthickness 0 \
335            -image [Rappture::icon zoom-out] \
336            -command [itcl::code $this Zoom out]
337    } {
338        usual
339        ignore -highlightthickness
340    }
341    pack $itk_component(zoomout) -side top -padx 2 -pady 2
342    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
343
344    itk_component add volume {
345        Rappture::PushButton $f.volume \
346            -onimage [Rappture::icon volume-on] \
347            -offimage [Rappture::icon volume-off] \
348            -variable [itcl::scope _settings(volumeVisible)] \
349            -command [itcl::code $this AdjustSetting volumeVisible]
350    }
351    $itk_component(volume) select
352    Rappture::Tooltip::for $itk_component(volume) \
353        "Don't display the volume"
354    pack $itk_component(volume) -padx 2 -pady 2
355
356    itk_component add streamlines {
357        Rappture::PushButton $f.streamlines \
358            -onimage [Rappture::icon streamlines-on] \
359            -offimage [Rappture::icon streamlines-off] \
360            -variable [itcl::scope _settings(streamlinesVisible)] \
361            -command [itcl::code $this AdjustSetting streamlinesVisible] \
362    }
363    $itk_component(streamlines) select
364    Rappture::Tooltip::for $itk_component(streamlines) \
365        "Toggle the streamlines on/off"
366    pack $itk_component(streamlines) -padx 2 -pady 2
367
368    itk_component add cutplane {
369        Rappture::PushButton $f.cutplane \
370            -onimage [Rappture::icon cutbutton] \
371            -offimage [Rappture::icon cutbutton] \
372            -variable [itcl::scope _settings(cutplaneVisible)] \
373            -command [itcl::code $this AdjustSetting cutplaneVisible]
374    }
375    Rappture::Tooltip::for $itk_component(cutplane) \
376        "Show/Hide cutplanes"
377    pack $itk_component(cutplane) -padx 2 -pady 2
378
379
380    if { [catch {
381        BuildVolumeTab
382        BuildStreamsTab
383        BuildCutplaneTab
384        BuildAxisTab
385        BuildCameraTab
386    } errs] != 0 } {
387        puts stderr errs=$errs
388    }
389    # Legend
390
391    set _image(legend) [image create photo]
392    itk_component add legend {
393        canvas $itk_component(plotarea).legend -width 50 -highlightthickness 0
394    } {
395        usual
396        ignore -highlightthickness
397        rename -background -plotbackground plotBackground Background
398    }
399
400    # Hack around the Tk panewindow.  The problem is that the requested
401    # size of the 3d view isn't set until an image is retrieved from
402    # the server.  So the panewindow uses the tiny size.
403    set w 10000
404    pack forget $itk_component(view)
405    blt::table $itk_component(plotarea) \
406        0,0 $itk_component(view) -fill both -reqwidth $w
407    blt::table configure $itk_component(plotarea) c1 -resize none
408
409    # Bindings for rotation via mouse
410    bind $itk_component(view) <ButtonPress-1> \
411        [itcl::code $this Rotate click %x %y]
412    bind $itk_component(view) <B1-Motion> \
413        [itcl::code $this Rotate drag %x %y]
414    bind $itk_component(view) <ButtonRelease-1> \
415        [itcl::code $this Rotate release %x %y]
416    bind $itk_component(view) <Configure> \
417        [itcl::code $this EventuallyResize %w %h]
418
419    if 0 {
420    bind $itk_component(view) <Configure> \
421        [itcl::code $this EventuallyResize %w %h]
422    }
423    # Bindings for panning via mouse
424    bind $itk_component(view) <ButtonPress-2> \
425        [itcl::code $this Pan click %x %y]
426    bind $itk_component(view) <B2-Motion> \
427        [itcl::code $this Pan drag %x %y]
428    bind $itk_component(view) <ButtonRelease-2> \
429        [itcl::code $this Pan release %x %y]
430
431    bind $itk_component(view) <ButtonRelease-3> \
432        [itcl::code $this Pick %x %y]
433
434    # Bindings for panning via keyboard
435    bind $itk_component(view) <KeyPress-Left> \
436        [itcl::code $this Pan set -10 0]
437    bind $itk_component(view) <KeyPress-Right> \
438        [itcl::code $this Pan set 10 0]
439    bind $itk_component(view) <KeyPress-Up> \
440        [itcl::code $this Pan set 0 -10]
441    bind $itk_component(view) <KeyPress-Down> \
442        [itcl::code $this Pan set 0 10]
443    bind $itk_component(view) <Shift-KeyPress-Left> \
444        [itcl::code $this Pan set -2 0]
445    bind $itk_component(view) <Shift-KeyPress-Right> \
446        [itcl::code $this Pan set 2 0]
447    bind $itk_component(view) <Shift-KeyPress-Up> \
448        [itcl::code $this Pan set 0 -2]
449    bind $itk_component(view) <Shift-KeyPress-Down> \
450        [itcl::code $this Pan set 0 2]
451
452    # Bindings for zoom via keyboard
453    bind $itk_component(view) <KeyPress-Prior> \
454        [itcl::code $this Zoom out]
455    bind $itk_component(view) <KeyPress-Next> \
456        [itcl::code $this Zoom in]
457
458    bind $itk_component(view) <Enter> "focus $itk_component(view)"
459
460    if {[string equal "x11" [tk windowingsystem]]} {
461        # Bindings for zoom via mouse
462        bind $itk_component(view) <4> [itcl::code $this Zoom out]
463        bind $itk_component(view) <5> [itcl::code $this Zoom in]
464    }
465
466    set _image(download) [image create photo]
467
468    eval itk_initialize $args
469    Connect
470    update
471}
472
473# ----------------------------------------------------------------------
474# DESTRUCTOR
475# ----------------------------------------------------------------------
476itcl::body Rappture::VtkStreamlinesViewer::destructor {} {
477    Disconnect
478    image delete $_image(plot)
479    image delete $_image(download)
480    catch { blt::arcball destroy $_arcball }
481}
482
483itcl::body Rappture::VtkStreamlinesViewer::DoResize {} {
484    if { $_width < 2 } {
485        set _width 500
486    }
487    if { $_height < 2 } {
488        set _height 500
489    }
490    set _start [clock clicks -milliseconds]
491    SendCmd "screen size $_width $_height"
492    set _legendPending 1
493
494    #SendCmd "imgflush"
495
496    # Must reset camera to have object scaling to take effect.
497    #SendCmd "camera reset"
498    #SendCmd "camera zoom $_view(zoom)"
499    set _resizePending 0
500}
501
502itcl::body Rappture::VtkStreamlinesViewer::DoRotate {} {
503    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
504    SendCmd "camera orient $q"
505    set _rotatePending 0
506}
507
508itcl::body Rappture::VtkStreamlinesViewer::DoReseed {} {
509    foreach dataset [CurrentDatasets -visible] {
510        foreach {dataobj comp} [split $dataset -] break
511        # This command works for either random or fmesh seeds
512        SendCmd "streamlines seed numpts $_numSeeds $dataset"
513    }
514    set _reseedPending 0
515}
516
517itcl::body Rappture::VtkStreamlinesViewer::EventuallyResize { w h } {
518    set _width $w
519    set _height $h
520    $_arcball resize $w $h
521    if { !$_resizePending } {
522        set _resizePending 1
523        $_dispatcher event -after 400 !resize
524    }
525}
526
527itcl::body Rappture::VtkStreamlinesViewer::EventuallyReseed { numPoints } {
528    set _numSeeds $numPoints
529    if { !$_reseedPending } {
530        set _reseedPending 1
531        $_dispatcher event -after 600 !reseed
532    }
533}
534
535set rotate_delay 100
536
537itcl::body Rappture::VtkStreamlinesViewer::EventuallyRotate { q } {
538    foreach { _view(qw) _view(qx) _view(qy) _view(qz) } $q break
539    if { !$_rotatePending } {
540        set _rotatePending 1
541        global rotate_delay
542        $_dispatcher event -after $rotate_delay !rotate
543    }
544}
545
546itcl::body Rappture::VtkStreamlinesViewer::EventuallySetCutplane { axis args } {
547    if { !$_cutplanePending } {
548        set _cutplanePending 1
549        $_dispatcher event -after 100 !${axis}cutplane
550    }
551}
552
553# ----------------------------------------------------------------------
554# USAGE: add <dataobj> ?<settings>?
555#
556# Clients use this to add a data object to the plot.  The optional
557# <settings> are used to configure the plot.  Allowed settings are
558# -color, -brightness, -width, -linestyle, and -raise.
559# ----------------------------------------------------------------------
560itcl::body Rappture::VtkStreamlinesViewer::add {dataobj {settings ""}} {
561    array set params {
562        -color auto
563        -width 1
564        -linestyle solid
565        -brightness 0
566        -raise 0
567        -description ""
568        -param ""
569        -type ""
570    }
571    array set params $settings
572    set params(-description) ""
573    set params(-param) ""
574    foreach {opt val} $settings {
575        if {![info exists params($opt)]} {
576            error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]"
577        }
578        set params($opt) $val
579    }
580    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
581        # can't handle -autocolors yet
582        set params(-color) black
583    }
584    set pos [lsearch -exact $dataobj $_dlist]
585    if {$pos < 0} {
586        lappend _dlist $dataobj
587    }
588    set _obj2ovride($dataobj-color) $params(-color)
589    set _obj2ovride($dataobj-width) $params(-width)
590    set _obj2ovride($dataobj-raise) $params(-raise)
591    $_dispatcher event -idle !rebuild
592}
593
594
595# ----------------------------------------------------------------------
596# USAGE: delete ?<dataobj1> <dataobj2> ...?
597#
598#       Clients use this to delete a dataobj from the plot.  If no dataobjs
599#       are specified, then all dataobjs are deleted.  No data objects are
600#       deleted.  They are only removed from the display list.
601#
602# ----------------------------------------------------------------------
603itcl::body Rappture::VtkStreamlinesViewer::delete {args} {
604    if { [llength $args] == 0} {
605        set args $_dlist
606    }
607    # Delete all specified dataobjs
608    set changed 0
609    foreach dataobj $args {
610        set pos [lsearch -exact $_dlist $dataobj]
611        if { $pos < 0 } {
612            continue;                   # Don't know anything about it.
613        }
614        # Remove it from the dataobj list.
615        set _dlist [lreplace $_dlist $pos $pos]
616        SendCmd "dataset visible 0"
617        array unset _obj2ovride $dataobj-*
618        array unset _settings $dataobj-*
619        # Append to the end of the dataobj list.
620        lappend _dlist $dataobj
621        set changed 1
622    }
623    # If anything changed, then rebuild the plot
624    if { $changed } {
625        $_dispatcher event -idle !rebuild
626    }
627}
628
629# ----------------------------------------------------------------------
630# USAGE: get ?-objects?
631# USAGE: get ?-visible?
632# USAGE: get ?-image view?
633#
634# Clients use this to query the list of objects being plotted, in
635# order from bottom to top of this result.  The optional "-image"
636# flag can also request the internal images being shown.
637# ----------------------------------------------------------------------
638itcl::body Rappture::VtkStreamlinesViewer::get {args} {
639    if {[llength $args] == 0} {
640        set args "-objects"
641    }
642
643    set op [lindex $args 0]
644    switch -- $op {
645        "-objects" {
646            # put the dataobj list in order according to -raise options
647            set dlist {}
648            foreach dataobj $_dlist {
649                if { ![IsValidObject $dataobj] } {
650                    continue
651                }
652                if {[info exists _obj2ovride($dataobj-raise)] &&
653                    $_obj2ovride($dataobj-raise)} {
654                    set dlist [linsert $dlist 0 $dataobj]
655                } else {
656                    lappend dlist $dataobj
657                }
658            }
659            return $dlist
660        }
661        "-visible" {
662            set dlist {}
663            foreach dataobj $_dlist {
664                if { ![IsValidObject $dataobj] } {
665                    continue
666                }
667                if { ![info exists _obj2ovride($dataobj-raise)] } {
668                    # No setting indicates that the object isn't visible.
669                    continue
670                }
671                # Otherwise use the -raise parameter to put the object to
672                # the front of the list.
673                if { $_obj2ovride($dataobj-raise) } {
674                    set dlist [linsert $dlist 0 $dataobj]
675                } else {
676                    lappend dlist $dataobj
677                }
678            }
679            return $dlist
680        }           
681        -image {
682            if {[llength $args] != 2} {
683                error "wrong # args: should be \"get -image view\""
684            }
685            switch -- [lindex $args end] {
686                view {
687                    return $_image(plot)
688                }
689                default {
690                    error "bad image name \"[lindex $args end]\": should be view"
691                }
692            }
693        }
694        default {
695            error "bad option \"$op\": should be -objects or -image"
696        }
697    }
698}
699
700# ----------------------------------------------------------------------
701# USAGE: scale ?<data1> <data2> ...?
702#
703# Sets the default limits for the overall plot according to the
704# limits of the data for all of the given <data> objects.  This
705# accounts for all objects--even those not showing on the screen.
706# Because of this, the limits are appropriate for all objects as
707# the user scans through data in the ResultSet viewer.
708# ----------------------------------------------------------------------
709itcl::body Rappture::VtkStreamlinesViewer::scale {args} {
710    array unset _limits
711    foreach dataobj $args {
712        set string [limits $dataobj]
713        if { $string == "" } {
714            continue
715        }
716        array set bounds $string
717        if {![info exists _limits(xmin)] || $_limits(xmin) > $bounds(xmin)} {
718            set _limits(xmin) $bounds(xmin)
719        }
720        if {![info exists _limits(xmax)] || $_limits(xmax) < $bounds(xmax)} {
721            set _limits(xmax) $bounds(xmax)
722        }
723
724        if {![info exists _limits(ymin)] || $_limits(ymin) > $bounds(ymin)} {
725            set _limits(ymin) $bounds(ymin)
726        }
727        if {![info exists _limits(ymax)] || $_limits(ymax) < $bounds(ymax)} {
728            set _limits(ymax) $bounds(ymax)
729        }
730
731        if {![info exists _limits(zmin)] || $_limits(zmin) > $bounds(zmin)} {
732            set _limits(zmin) $bounds(zmin)
733        }
734        if {![info exists _limits(zmax)] || $_limits(zmax) < $bounds(zmax)} {
735            set _limits(zmax) $bounds(zmax)
736        }
737    }
738}
739
740# ----------------------------------------------------------------------
741# USAGE: download coming
742# USAGE: download controls <downloadCommand>
743# USAGE: download now
744#
745# Clients use this method to create a downloadable representation
746# of the plot.  Returns a list of the form {ext string}, where
747# "ext" is the file extension (indicating the type of data) and
748# "string" is the data itself.
749# ----------------------------------------------------------------------
750itcl::body Rappture::VtkStreamlinesViewer::download {option args} {
751    switch $option {
752        coming {
753            if {[catch {
754                blt::winop snap $itk_component(plotarea) $_image(download)
755            }]} {
756                $_image(download) configure -width 1 -height 1
757                $_image(download) put #000000
758            }
759        }
760        controls {
761            set popup .vtkviewerdownload
762            if { ![winfo exists .vtkviewerdownload] } {
763                set inner [BuildDownloadPopup $popup [lindex $args 0]]
764            } else {
765                set inner [$popup component inner]
766            }
767            set _downloadPopup(image_controls) $inner.image_frame
768            set num [llength [get]]
769            set num [expr {($num == 1) ? "1 result" : "$num results"}]
770            set word [Rappture::filexfer::label downloadWord]
771            $inner.summary configure -text "$word $num in the following format:"
772            update idletasks            ;# Fix initial sizes
773            return $popup
774        }
775        now {
776            set popup .vtkviewerdownload
777            if {[winfo exists .vtkviewerdownload]} {
778                $popup deactivate
779            }
780            switch -- $_downloadPopup(format) {
781                "image" {
782                    return [$this GetImage [lindex $args 0]]
783                }
784                "vtk" {
785                    return [$this GetVtkData [lindex $args 0]]
786                }
787            }
788            return ""
789        }
790        default {
791            error "bad option \"$option\": should be coming, controls, now"
792        }
793    }
794}
795
796# ----------------------------------------------------------------------
797# USAGE: Connect ?<host:port>,<host:port>...?
798#
799# Clients use this method to establish a connection to a new
800# server, or to reestablish a connection to the previous server.
801# Any existing connection is automatically closed.
802# ----------------------------------------------------------------------
803itcl::body Rappture::VtkStreamlinesViewer::Connect {} {
804    set _hosts [GetServerList "vtkvis"]
805    if { "" == $_hosts } {
806        return 0
807    }
808    set result [VisViewer::Connect $_hosts]
809    if { $result } {
810        set w [winfo width $itk_component(view)]
811        set h [winfo height $itk_component(view)]
812        EventuallyResize $w $h
813    }
814    return $result
815}
816
817#
818# isconnected --
819#
820#       Indicates if we are currently connected to the visualization server.
821#
822itcl::body Rappture::VtkStreamlinesViewer::isconnected {} {
823    return [VisViewer::IsConnected]
824}
825
826#
827# disconnect --
828#
829itcl::body Rappture::VtkStreamlinesViewer::disconnect {} {
830    Disconnect
831    set _reset 1
832}
833
834#
835# Disconnect --
836#
837#       Clients use this method to disconnect from the current rendering
838#       server.
839#
840itcl::body Rappture::VtkStreamlinesViewer::Disconnect {} {
841    VisViewer::Disconnect
842
843    $_dispatcher cancel !rebuild
844    $_dispatcher cancel !resize
845    $_dispatcher cancel !reseed
846    $_dispatcher cancel !rotate
847    $_dispatcher cancel !xcutplane
848    $_dispatcher cancel !ycutplane
849    $_dispatcher cancel !zcutplane
850    $_dispatcher cancel !legend
851    # disconnected -- no more data sitting on server
852    set _outbuf ""
853    array unset _datasets
854    array unset _data
855    array unset _colormaps
856    array unset _seeds
857    array unset _dataset2style
858    array unset _obj2datasets
859}
860
861#
862# sendto --
863#
864itcl::body Rappture::VtkStreamlinesViewer::sendto { bytes } {
865    SendBytes "$bytes\n"
866    StartWaiting
867}
868
869#
870# SendCmd
871#
872#       Send commands off to the rendering server.  If we're currently
873#       sending data objects to the server, buffer the commands to be
874#       sent later.
875#
876itcl::body Rappture::VtkStreamlinesViewer::SendCmd {string} {
877    if { $_buffering } {
878        append _outbuf $string "\n"
879    } else {
880        SendBytes "$string\n"
881        StartWaiting
882    }
883}
884
885#
886# SendCmdNoSplash
887#
888#       Send commands off to the rendering server.  This method
889#       doesn't initiate the slash screen.
890#
891itcl::body Rappture::VtkStreamlinesViewer::SendCmdNoSplash {string} {
892    if { $_buffering } {
893        append _outbuf $string "\n"
894    } else {
895        SendBytes "$string\n"
896    }
897}
898
899# ----------------------------------------------------------------------
900# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
901#
902# Invoked automatically whenever the "image" command comes in from
903# the rendering server.  Indicates that binary image data with the
904# specified <size> will follow.
905# ----------------------------------------------------------------------
906itcl::body Rappture::VtkStreamlinesViewer::ReceiveImage { args } {
907    array set info {
908        -token "???"
909        -bytes 0
910        -type image
911    }
912    array set info $args
913    set bytes [ReceiveBytes $info(-bytes)]
914    StopWaiting
915    if { $info(-type) == "image" } {
916        if 0 {
917            set f [open "last.ppm" "w"]
918            puts $f $bytes
919            close $f
920        }
921        $_image(plot) configure -data $bytes
922        set time [clock seconds]
923        set date [clock format $time]
924        #puts stderr "$date: received image [image width $_image(plot)]x[image height $_image(plot)] image>"       
925        if { $_start > 0 } {
926            set finish [clock clicks -milliseconds]
927            #puts stderr "round trip time [expr $finish -$_start] milliseconds"
928            set _start 0
929        }
930    } elseif { $info(type) == "print" } {
931        set tag $this-print-$info(-token)
932        set _hardcopy($tag) $bytes
933    }
934    if { $_legendPending } {
935        RequestLegend
936    }
937}
938
939#
940# ReceiveDataset --
941#
942itcl::body Rappture::VtkStreamlinesViewer::ReceiveDataset { args } {
943    if { ![isconnected] } {
944        return
945    }
946    set option [lindex $args 0]
947    switch -- $option {
948        "scalar" {
949            set option [lindex $args 1]
950            switch -- $option {
951                "world" {
952                    foreach { x y z value tag } [lrange $args 2 end] break
953                }
954                "pixel" {
955                    foreach { x y value tag } [lrange $args 2 end] break
956                }
957            }
958        }
959        "vector" {
960            set option [lindex $args 1]
961            switch -- $option {
962                "world" {
963                    foreach { x y z vx vy vz tag } [lrange $args 2 end] break
964                }
965                "pixel" {
966                    foreach { x y vx vy vz tag } [lrange $args 2 end] break
967                }
968            }
969        }
970        "names" {
971            foreach { name } [lindex $args 1] {
972                #puts stderr "Dataset: $name"
973            }
974        }
975        default {
976            error "unknown dataset option \"$option\" from server"
977        }
978    }
979}
980
981# ----------------------------------------------------------------------
982# USAGE: Rebuild
983#
984# Called automatically whenever something changes that affects the
985# data in the widget.  Clears any existing data and rebuilds the
986# widget to display new data.
987# ----------------------------------------------------------------------
988itcl::body Rappture::VtkStreamlinesViewer::Rebuild {} {
989    update
990    set w [winfo width $itk_component(view)]
991    set h [winfo height $itk_component(view)]
992    if { $w < 2 || $h < 2 } {
993        $_dispatcher event -idle !rebuild
994        return
995    }
996
997    set _buffering 1
998    set _legendPending 1
999    # Turn on buffering of commands to the server.  We don't want to
1000    # be preempted by a server disconnect/reconnect (which automatically
1001    # generates a new call to Rebuild).   
1002
1003    set _first ""
1004    if { $_reset } {
1005        set _width $w
1006        set _height $h
1007        $_arcball resize $w $h
1008        DoResize
1009        InitSettings axisXGrid axisYGrid axisZGrid axis-mode \
1010            axesVisible axisLabelsVisible \
1011    }
1012    #SendCmd "imgflush"
1013
1014    set _limits(zmin) ""
1015    set _limits(zmax) ""
1016    set _first ""
1017    foreach dataobj [get -objects] {
1018        if { [info exists _obj2ovride($dataobj-raise)] &&  $_first == "" } {
1019            set _first $dataobj
1020        }
1021        set _obj2datasets($dataobj) ""
1022        foreach comp [$dataobj components] {
1023            set tag $dataobj-$comp
1024            if { ![info exists _datasets($tag)] } {
1025                set bytes [$dataobj vtkdata $comp]
1026                set length [string length $bytes]
1027                append _outbuf "dataset add $tag data follows $length\n"
1028                append _outbuf $bytes
1029                set _datasets($tag) 1
1030                SetObjectStyle $dataobj $comp
1031            }
1032            lappend _obj2datasets($dataobj) $tag
1033            if { [info exists _obj2ovride($dataobj-raise)] } {
1034                SendCmd "dataset visible 1 $tag"
1035            } else {
1036                SendCmd "dataset visible 0 $tag"
1037            }
1038        }
1039    }
1040    if {"" != $_first} {
1041        set location [$_first hints camera]
1042        if { $location != "" } {
1043            array set view $location
1044        }
1045        foreach axis { x y z } {
1046            set label [$_first hints ${axis}label]
1047            if { $label != "" } {
1048                SendCmd "axis name $axis $label"
1049            }
1050            set units [$_first hints ${axis}units]
1051            if { $units != "" } {
1052                SendCmd "axis units $axis $units"
1053            }
1054        }
1055        array unset _scalarFields
1056        array unset _vectorFields
1057        set _currentField [$_first hints default]
1058        $itk_component(field) choices delete 0 end
1059        $itk_component(fieldmenu) delete 0 end
1060        array unset _fields
1061        foreach { name title units } [$_first hints vectors] {
1062            set _vectorFields($title) $name
1063            $itk_component(field) choices insert end "$name" "$title"
1064            $itk_component(fieldmenu) add radiobutton -label "$title" \
1065                -value $title -variable [itcl::scope _currentField] \
1066                -selectcolor red \
1067                -activebackground black \
1068                -activeforeground white \
1069                -font "Arial 8" \
1070                -command [itcl::code $this Combo invoke]
1071            set _fields($name) [list $title $units]
1072        }
1073        foreach { name title units } [$_first hints scalars] {
1074            set _scalarFields($title) $name
1075            $itk_component(field) choices insert end "$name" "$title"
1076            $itk_component(fieldmenu) add radiobutton -label "$title" \
1077                -value $title -variable [itcl::scope _currentField] \
1078                -selectcolor red \
1079                -activebackground black \
1080                -activeforeground white \
1081                -font "Arial 8" \
1082                -command [itcl::code $this Combo invoke]
1083            set _fields($name) [list $title $units]
1084        }
1085        $itk_component(field) value $_currentField
1086    }
1087
1088    InitSettings streamlinesVisible streamlines-palette volumeVisible
1089
1090    if { $_reset } {
1091        if 1 {
1092            # Tell the server the name of the tool, the version, and dataset
1093            # that we are rendering.  Have to do it here because we don't know
1094            # what data objects are using the renderer until be get here.
1095            global env
1096
1097            lappend data "hub" [exec hostname]
1098            lappend data "viewer" "vkstreamlinesviewer"
1099            if { [info exists env(USER)] } {
1100                lappend data "user" $env(USER)
1101            }
1102            if { [info exists env(SESSION)] } {
1103                lappend data "session" $env(SESSION)
1104            }
1105            lappend data "tool_id"      [$_first hints toolId]
1106            lappend data "tool_name"    [$_first hints toolName]
1107            lappend data "tool_version" [$_first hints toolRevision]
1108            lappend data "tool_title"   [$_first hints toolTitle]
1109            lappend data "tool_dataset" [$_first hints label]
1110            SendCmd "clientinfo [list $data]"
1111        }
1112        InitSettings streamlinesSeedsVisible streamlinesOpacity \
1113            streamlinesNumSeeds streamlinesLighting \
1114            streamlines-palette streamlines-field \
1115            volumeEdges volumeLighting volumeOpacity volumeWireframe \
1116            cutplaneVisible \
1117            cutplaneXPosition cutplaneYPosition cutplaneZPosition \
1118            cutplaneXVisible cutplaneYVisible cutplaneZVisible
1119        #
1120        # Reset the camera and other view parameters
1121        #
1122        set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
1123        $_arcball quaternion $q
1124        if {$_view(ortho)} {
1125            SendCmd "camera mode ortho"
1126        } else {
1127            SendCmd "camera mode persp"
1128        }
1129        DoRotate
1130        PanCamera
1131        Zoom reset
1132        set _reset 0
1133    }
1134    set _buffering 0;                        # Turn off buffering.
1135
1136    # Actually write the commands to the server socket.  If it fails, we don't
1137    # care.  We're finished here.
1138    blt::busy hold $itk_component(hull)
1139    sendto $_outbuf;                       
1140    blt::busy release $itk_component(hull)
1141    set _outbuf "";                        # Clear the buffer.               
1142}
1143
1144# ----------------------------------------------------------------------
1145# USAGE: CurrentDatasets ?-all -visible? ?dataobjs?
1146#
1147# Returns a list of server IDs for the current datasets being displayed.  This
1148# is normally a single ID, but it might be a list of IDs if the current data
1149# object has multiple components.
1150# ----------------------------------------------------------------------
1151itcl::body Rappture::VtkStreamlinesViewer::CurrentDatasets {args} {
1152    set flag [lindex $args 0]
1153    switch -- $flag {
1154        "-all" {
1155            if { [llength $args] > 1 } {
1156                error "CurrentDatasets: can't specify dataobj after \"-all\""
1157            }
1158            set dlist [get -objects]
1159        }
1160        "-visible" {
1161            if { [llength $args] > 1 } {
1162                set dlist {}
1163                set args [lrange $args 1 end]
1164                foreach dataobj $args {
1165                    if { [info exists _obj2ovride($dataobj-raise)] } {
1166                        lappend dlist $dataobj
1167                    }
1168                }
1169            } else {
1170                set dlist [get -visible]
1171            }
1172        }           
1173        default {
1174            set dlist $args
1175        }
1176    }
1177    set rlist ""
1178    foreach dataobj $dlist {
1179        foreach comp [$dataobj components] {
1180            set tag $dataobj-$comp
1181            if { [info exists _datasets($tag)] && $_datasets($tag) } {
1182                lappend rlist $tag
1183            }
1184        }
1185    }
1186    return $rlist
1187}
1188
1189# ----------------------------------------------------------------------
1190# USAGE: Zoom in
1191# USAGE: Zoom out
1192# USAGE: Zoom reset
1193#
1194# Called automatically when the user clicks on one of the zoom
1195# controls for this widget.  Changes the zoom for the current view.
1196# ----------------------------------------------------------------------
1197itcl::body Rappture::VtkStreamlinesViewer::Zoom {option} {
1198    switch -- $option {
1199        "in" {
1200            set _view(zoom) [expr {$_view(zoom)*1.25}]
1201            SendCmd "camera zoom $_view(zoom)"
1202        }
1203        "out" {
1204            set _view(zoom) [expr {$_view(zoom)*0.8}]
1205            SendCmd "camera zoom $_view(zoom)"
1206        }
1207        "reset" {
1208            array set _view {
1209                qw              0.853553
1210                qx              -0.353553
1211                qy              0.353553
1212                qz              0.146447
1213                zoom    1.0
1214                xpan   0
1215                ypan   0
1216            }
1217            SendCmd "camera reset all"
1218            if { $_first != "" } {
1219                set location [$_first hints camera]
1220                if { $location != "" } {
1221                    array set _view $location
1222                }
1223            }
1224            set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
1225            $_arcball quaternion $q
1226            DoRotate
1227        }
1228    }
1229}
1230
1231itcl::body Rappture::VtkStreamlinesViewer::PanCamera {} {
1232    set x $_view(xpan)
1233    set y $_view(ypan)
1234    SendCmd "camera pan $x $y"
1235}
1236
1237
1238# ----------------------------------------------------------------------
1239# USAGE: Rotate click <x> <y>
1240# USAGE: Rotate drag <x> <y>
1241# USAGE: Rotate release <x> <y>
1242#
1243# Called automatically when the user clicks/drags/releases in the
1244# plot area.  Moves the plot according to the user's actions.
1245# ----------------------------------------------------------------------
1246itcl::body Rappture::VtkStreamlinesViewer::Rotate {option x y} {
1247    switch -- $option {
1248        "click" {
1249            $itk_component(view) configure -cursor fleur
1250            set _click(x) $x
1251            set _click(y) $y
1252        }
1253        "drag" {
1254            if {[array size _click] == 0} {
1255                Rotate click $x $y
1256            } else {
1257                set w [winfo width $itk_component(view)]
1258                set h [winfo height $itk_component(view)]
1259                if {$w <= 0 || $h <= 0} {
1260                    return
1261                }
1262
1263                if {[catch {
1264                    # this fails sometimes for no apparent reason
1265                    set dx [expr {double($x-$_click(x))/$w}]
1266                    set dy [expr {double($y-$_click(y))/$h}]
1267                }]} {
1268                    return
1269                }
1270                if { $dx == 0 && $dy == 0 } {
1271                    return
1272                }
1273                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1274                EventuallyRotate $q
1275                set _click(x) $x
1276                set _click(y) $y
1277            }
1278        }
1279        "release" {
1280            Rotate drag $x $y
1281            $itk_component(view) configure -cursor ""
1282            catch {unset _click}
1283        }
1284        default {
1285            error "bad option \"$option\": should be click, drag, release"
1286        }
1287    }
1288}
1289
1290itcl::body Rappture::VtkStreamlinesViewer::Pick {x y} {
1291    foreach tag [CurrentDatasets -visible] {
1292        SendCmdNoSplash "dataset getscalar pixel $x $y $tag"
1293    }
1294}
1295
1296# ----------------------------------------------------------------------
1297# USAGE: $this Pan click x y
1298#        $this Pan drag x y
1299#        $this Pan release x y
1300#
1301# Called automatically when the user clicks on one of the zoom
1302# controls for this widget.  Changes the zoom for the current view.
1303# ----------------------------------------------------------------------
1304itcl::body Rappture::VtkStreamlinesViewer::Pan {option x y} {
1305    switch -- $option {
1306        "set" {
1307            set w [winfo width $itk_component(view)]
1308            set h [winfo height $itk_component(view)]
1309            set x [expr $x / double($w)]
1310            set y [expr $y / double($h)]
1311            set _view(xpan) [expr $_view(xpan) + $x]
1312            set _view(ypan) [expr $_view(ypan) + $y]
1313            PanCamera
1314            return
1315        }
1316        "click" {
1317            set _click(x) $x
1318            set _click(y) $y
1319            $itk_component(view) configure -cursor hand1
1320        }
1321        "drag" {
1322            if { ![info exists _click(x)] } {
1323                set _click(x) $x
1324            }
1325            if { ![info exists _click(y)] } {
1326                set _click(y) $y
1327            }
1328            set w [winfo width $itk_component(view)]
1329            set h [winfo height $itk_component(view)]
1330            set dx [expr ($_click(x) - $x)/double($w)]
1331            set dy [expr ($_click(y) - $y)/double($h)]
1332            set _click(x) $x
1333            set _click(y) $y
1334            set _view(xpan) [expr $_view(xpan) - $dx]
1335            set _view(ypan) [expr $_view(ypan) - $dy]
1336            PanCamera
1337        }
1338        "release" {
1339            Pan drag $x $y
1340            $itk_component(view) configure -cursor ""
1341        }
1342        default {
1343            error "unknown option \"$option\": should set, click, drag, or release"
1344        }
1345    }
1346}
1347
1348# ----------------------------------------------------------------------
1349# USAGE: InitSettings <what> ?<value>?
1350#
1351# Used internally to update rendering settings whenever parameters
1352# change in the popup settings panel.  Sends the new settings off
1353# to the back end.
1354# ----------------------------------------------------------------------
1355itcl::body Rappture::VtkStreamlinesViewer::InitSettings { args } {
1356    foreach spec $args {
1357        if { [info exists _settings($_first-$spec)] } {
1358            # Reset global setting with dataobj specific setting
1359            set _settings($spec) $_settings($_first-$spec)
1360        }
1361        AdjustSetting $spec
1362    }
1363}
1364
1365#
1366# AdjustSetting --
1367#
1368#       Changes/updates a specific setting in the widget.  There are
1369#       usually user-setable option.  Commands are sent to the render
1370#       server.
1371#
1372itcl::body Rappture::VtkStreamlinesViewer::AdjustSetting {what {value ""}} {
1373    if { ![isconnected] } {
1374        return
1375    }
1376    switch -- $what {
1377        "volumeOpacity" {
1378            set val $_settings(volumeOpacity)
1379            set sval [expr { 0.01 * double($val) }]
1380            foreach dataset [CurrentDatasets -visible] {
1381                SendCmd "polydata opacity $sval $dataset"
1382            }
1383        }
1384        "volumeWireframe" {
1385            set bool $_settings(volumeWireframe)
1386            foreach dataset [CurrentDatasets -visible] {
1387                SendCmd "polydata wireframe $bool $dataset"
1388            }
1389        }
1390        "volumeVisible" {
1391            set bool $_settings(volumeVisible)
1392            foreach dataset [CurrentDatasets -visible] {
1393                SendCmd "polydata visible $bool $dataset"
1394            }
1395            if { $bool } {
1396                Rappture::Tooltip::for $itk_component(volume) \
1397                    "Hide the volume"
1398            } else {
1399                Rappture::Tooltip::for $itk_component(volume) \
1400                    "Show the volume"
1401            }
1402        }
1403        "volumeLighting" {
1404            set bool $_settings(volumeLighting)
1405            foreach dataset [CurrentDatasets -visible] {
1406                SendCmd "polydata lighting $bool $dataset"
1407            }
1408        }
1409        "volumeEdges" {
1410            set bool $_settings(volumeEdges)
1411            foreach dataset [CurrentDatasets -visible] {
1412                SendCmd "polydata edges $bool $dataset"
1413            }
1414        }
1415        "axesVisible" {
1416            set bool $_settings(axesVisible)
1417            SendCmd "axis visible all $bool"
1418        }
1419        "axisLabelsVisible" {
1420            set bool $_settings(axisLabelsVisible)
1421            SendCmd "axis labels all $bool"
1422        }
1423        "axisXGrid" - "axisYGrid" - "axisZGrid" {
1424            set axis [string tolower [string range $what 4 4]]
1425            set bool $_settings($what)
1426            SendCmd "axis grid $axis $bool"
1427        }
1428        "axis-mode" {
1429            set mode [$itk_component(axismode) value]
1430            set mode [$itk_component(axismode) translate $mode]
1431            set _settings($what) $mode
1432            SendCmd "axis flymode $mode"
1433        }
1434        "cutplaneEdges" {
1435            set bool $_settings($what)
1436            foreach dataset [CurrentDatasets -visible] {
1437                SendCmd "cutplane edges $bool $dataset"
1438            }
1439        }
1440        "cutplaneVisible" {
1441            set bool $_settings($what)
1442            foreach dataset [CurrentDatasets -visible] {
1443                SendCmd "cutplane visible $bool $dataset"
1444            }
1445        }
1446        "cutplaneWireframe" {
1447            set bool $_settings($what)
1448            foreach dataset [CurrentDatasets -visible] {
1449                SendCmd "cutplane wireframe $bool $dataset"
1450            }
1451        }
1452        "cutplaneLighting" {
1453            set bool $_settings($what)
1454            foreach dataset [CurrentDatasets -visible] {
1455                SendCmd "cutplane lighting $bool $dataset"
1456            }
1457        }
1458        "cutplaneOpacity" {
1459            set val $_settings($what)
1460            set sval [expr { 0.01 * double($val) }]
1461            foreach dataset [CurrentDatasets -visible] {
1462                SendCmd "cutplane opacity $sval $dataset"
1463            }
1464        }
1465        "cutplaneXVisible" - "cutplaneYVisible" - "cutplaneZVisible" {
1466            set axis [string tolower [string range $what 8 8]]
1467            set bool $_settings($what)
1468            if { $bool } {
1469                $itk_component(${axis}CutScale) configure -state normal \
1470                    -troughcolor white
1471            } else {
1472                $itk_component(${axis}CutScale) configure -state disabled \
1473                    -troughcolor grey82
1474            }
1475            foreach dataset [CurrentDatasets -visible] {
1476                SendCmd "cutplane axis $axis $bool $dataset"
1477            }
1478        }
1479        "cutplaneXPosition" - "cutplaneYPosition" - "cutplaneZPosition" {
1480            set axis [string tolower [string range $what 8 8]]
1481            set pos [expr $_settings($what) * 0.01]
1482            foreach dataset [CurrentDatasets -visible] {
1483                SendCmd "cutplane slice ${axis} ${pos} $dataset"
1484            }
1485            set _cutplanePending 0
1486        }
1487        "streamlinesSeedsVisible" {
1488            set bool $_settings($what)
1489            foreach dataset [CurrentDatasets -visible] {
1490                SendCmd "streamlines seed visible $bool $dataset"
1491            }
1492        }
1493        "streamlinesNumSeeds" {
1494            set density $_settings($what)
1495            EventuallyReseed $density
1496        }
1497        "streamlinesVisible" {
1498            set bool $_settings($what)
1499            foreach dataset [CurrentDatasets -visible] {
1500                SendCmd "streamlines visible $bool $dataset"
1501            }
1502            if { $bool } {
1503                Rappture::Tooltip::for $itk_component(streamlines) \
1504                    "Hide the streamlines"
1505            } else {
1506                Rappture::Tooltip::for $itk_component(streamlines) \
1507                    "Show the streamlines"
1508            }
1509        }
1510        "streamlinesMode" {
1511            set mode [$itk_component(streammode) value]
1512            set _settings(streamlinesMode) $mode
1513            foreach dataset [CurrentDatasets -visible] {
1514                switch -- $mode {
1515                    "lines" {
1516                        SendCmd "streamlines lines $dataset"
1517                    }
1518                    "ribbons" {
1519                        SendCmd "streamlines ribbons 3 0 $dataset"
1520                    }
1521                    "tubes" {
1522                        SendCmd "streamlines tubes 5 3 $dataset"
1523                    }
1524                }
1525            }
1526        }
1527        "streamlines-palette" {
1528            set palette [$itk_component(palette) value]
1529            set _settings(streamlines-palette) $palette
1530            foreach dataset [CurrentDatasets -visible $_first] {
1531                foreach {dataobj comp} [split $dataset -] break
1532                ChangeColormap $dataobj $comp $palette
1533            }
1534            set _legendPending 1
1535        }
1536        "streamlinesOpacity" {
1537            set val $_settings(streamlinesOpacity)
1538            set sval [expr { 0.01 * double($val) }]
1539            foreach dataset [CurrentDatasets -visible $_first] {
1540                SendCmd "streamlines opacity $sval $dataset"
1541            }
1542        }
1543        "streamlinesScale" {
1544            set val $_settings(streamlinesScale)
1545            set sval [expr { 0.01 * double($val) }]
1546            foreach dataset [CurrentDatasets -visible $_first] {
1547                SendCmd "streamlines scale $sval $sval $sval $dataset"
1548            }
1549        }
1550        "streamlinesLighting" {
1551            set bool $_settings(streamlinesLighting)
1552            foreach dataset [CurrentDatasets -visible $_first] {
1553                SendCmd "streamlines lighting $bool $dataset"
1554            }
1555        }
1556        "streamlines-field" {
1557            set new [$itk_component(field) value]
1558            set value [$itk_component(field) translate $new]
1559            set _settings(streamlines-field) $value
1560            if { [info exists _scalarFields($new)] } {
1561                set name $_scalarFields($new)
1562                set _colorMode scalar
1563                set _currentField $new
1564            } elseif { [info exists _vectorFields($new)] } {
1565                set name $_vectorFields($new)
1566                set _colorMode vmag
1567                set _currentField $new
1568            } else {
1569                puts stderr "unknown field \"$new\""
1570                return
1571            }
1572            foreach dataset [CurrentDatasets -visible] {
1573                puts stderr "streamlines colormode $_colorMode ${name} $dataset"
1574                puts stderr "cutplane colormode $_colorMode ${name} $dataset"
1575                SendCmd "streamlines colormode $_colorMode ${name} $dataset"
1576                SendCmd "cutplane colormode $_colorMode ${name} $dataset"
1577            }
1578            set _legendPending 1
1579        }
1580        default {
1581            error "don't know how to fix $what"
1582        }
1583    }
1584}
1585
1586#
1587# RequestLegend --
1588#
1589#       Request a new legend from the server.  The size of the legend
1590#       is determined from the height of the canvas.  It will be rotated
1591#       to be vertical when drawn.
1592#
1593itcl::body Rappture::VtkStreamlinesViewer::RequestLegend {} {
1594    set font "Arial 8"
1595    set lineht [font metrics $font -linespace]
1596    set c $itk_component(legend)
1597    set w 12
1598    set h [expr {$_height - 3 * ($lineht + 2)}]
1599    if { $h < 1} {
1600        return
1601    }
1602    if { [info exists _scalarFields($_currentField)] } {
1603        set name $_scalarFields($_currentField)
1604    } elseif { [info exists _vectorFields($_currentField)] } {
1605        set name $_vectorFields($_currentField)
1606    } else {
1607        return
1608    }
1609    # Set the legend on the first streamlines dataset.
1610    foreach dataset [CurrentDatasets -visible $_first] {
1611        foreach {dataobj comp} [split $dataset -] break
1612        if { [info exists _dataset2style($dataset)] } {
1613            SendCmdNoSplash \
1614                "legend $_dataset2style($dataset) $_colorMode $name {} $w $h 0"
1615            break;
1616        }
1617    }
1618}
1619
1620#
1621# ChangeColormap --
1622#
1623itcl::body Rappture::VtkStreamlinesViewer::ChangeColormap {dataobj comp color} {
1624    set tag $dataobj-$comp
1625    if { ![info exist _style($tag)] } {
1626        error "no initial colormap"
1627    }
1628    array set style $_style($tag)
1629    set style(-color) $color
1630    set _style($tag) [array get style]
1631    SetColormap $dataobj $comp
1632}
1633
1634#
1635# SetColormap --
1636#
1637itcl::body Rappture::VtkStreamlinesViewer::SetColormap { dataobj comp } {
1638    array set style {
1639        -color BCGYR
1640        -levels 6
1641        -opacity 1.0
1642    }
1643    set tag $dataobj-$comp
1644    if { ![info exists _initialStyle($tag)] } {
1645        # Save the initial component style.
1646        set _initialStyle($tag) [$dataobj style $comp]
1647    }
1648
1649    # Override defaults with initial style defined in xml.
1650    array set style $_initialStyle($tag)
1651
1652    if { ![info exists _style($tag)] } {
1653        set _style($tag) [array get style]
1654    }
1655    # Override initial style with current style.
1656    array set style $_style($tag)
1657
1658    set name "$style(-color):$style(-levels):$style(-opacity)"
1659    if { ![info exists _colormaps($name)] } {
1660        BuildColormap $name [array get style]
1661        set _colormaps($name) 1
1662    }
1663    if { ![info exists _dataset2style($tag)] ||
1664         $_dataset2style($tag) != $name } {
1665        SendCmd "streamlines colormap $name $tag"
1666        SendCmd "cutplane colormap $name $tag"
1667        set _dataset2style($tag) $name
1668    }
1669}
1670
1671
1672#
1673# BuildColormap --
1674#
1675itcl::body Rappture::VtkStreamlinesViewer::BuildColormap { name styles } {
1676    array set style $styles
1677    set cmap [ColorsToColormap $style(-color)]
1678    if { [llength $cmap] == 0 } {
1679        set cmap "0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0"
1680    }
1681    if { ![info exists _settings(volumeOpacity)] } {
1682        set _settings(volumeOpacity) $style(-opacity)
1683    }
1684    set max $_settings(volumeOpacity)
1685
1686    set wmap "0.0 1.0 1.0 1.0"
1687    SendCmd "colormap add $name { $cmap } { $wmap }"
1688}
1689
1690# ----------------------------------------------------------------------
1691# CONFIGURATION OPTION: -plotbackground
1692# ----------------------------------------------------------------------
1693itcl::configbody Rappture::VtkStreamlinesViewer::plotbackground {
1694    if { [isconnected] } {
1695        foreach {r g b} [Color2RGB $itk_option(-plotbackground)] break
1696        SendCmd "screen bgcolor $r $g $b"
1697    }
1698}
1699
1700# ----------------------------------------------------------------------
1701# CONFIGURATION OPTION: -plotforeground
1702# ----------------------------------------------------------------------
1703itcl::configbody Rappture::VtkStreamlinesViewer::plotforeground {
1704    if { [isconnected] } {
1705        foreach {r g b} [Color2RGB $itk_option(-plotforeground)] break
1706        #fix this!
1707        #SendCmd "color background $r $g $b"
1708    }
1709}
1710
1711itcl::body Rappture::VtkStreamlinesViewer::limits { dataobj } {
1712    return
1713    array unset _limits $dataobj-*
1714    foreach comp [$dataobj components] {
1715        set tag $dataobj-$comp
1716        if { ![info exists _limits($tag)] } {
1717            set data [$dataobj blob $comp]
1718            set tmpfile file[pid].vtk
1719            set f [open "$tmpfile" "w"]
1720            fconfigure $f -translation binary -encoding binary
1721            puts $f $data
1722            close $f
1723            set reader [vtkDataSetReader $tag-xvtkDataSetReader]
1724            $reader SetFileName $tmpfile
1725            $reader ReadAllScalarsOn
1726            $reader ReadAllVectorsOn
1727            $reader ReadAllFieldsOn
1728            $reader Update
1729            set output [$reader GetOutput]
1730            set _limits($tag) [$output GetBounds]
1731            set pointData [$output GetPointData]
1732            puts stderr "\#scalars=[$reader GetNumberOfScalarsInFile]"
1733            puts stderr "\#fielddata=[$reader GetNumberOfFieldDataInFile]"
1734            puts stderr "fielddataname=[$reader GetFieldDataNameInFile 0]"
1735            set fieldData [$output GetFieldData]
1736            set pointData [$output GetPointData]
1737            puts stderr "field \#arrays=[$fieldData GetNumberOfArrays]"
1738            for { set i 0 } { $i < [$fieldData GetNumberOfArrays] } { incr i } {
1739                puts stderr [$fieldData GetArrayName $i]
1740            }
1741            puts stderr "point \#arrays=[$pointData GetNumberOfArrays]"
1742            for { set i 0 } { $i < [$pointData GetNumberOfArrays] } { incr i } {
1743                set name [$pointData GetArrayName $i]
1744                if { ![info exists _fields($name)] } {
1745                    $itk_component(field) choices insert end "$name" "$name"
1746                    set _fields($name) 1
1747                }
1748            }
1749            puts stderr "field \#components=[$fieldData GetNumberOfComponents]"
1750            puts stderr "point \#components=[$pointData GetNumberOfComponents]"
1751            puts stderr "field \#tuples=[$fieldData GetNumberOfTuples]"
1752            puts stderr "point \#tuples=[$pointData GetNumberOfTuples]"
1753            puts stderr "point \#scalars=[$pointData GetScalars]"
1754            puts stderr vectors=[$pointData GetVectors]
1755            rename $output ""
1756            rename $reader ""
1757            file delete $tmpfile
1758        }
1759        foreach { xMin xMax yMin yMax zMin zMax} $_limits($tag) break
1760        if {![info exists limits(xmin)] || $limits(xmin) > $xMin} {
1761            set limits(xmin) $xMin
1762        }
1763        if {![info exists limits(xmax)] || $limits(xmax) < $xMax} {
1764            set limits(xmax) $xMax
1765        }
1766        if {![info exists limits(ymin)] || $limits(ymin) > $yMin} {
1767            set limits(ymin) $xMin
1768        }
1769        if {![info exists limits(ymax)] || $limits(ymax) < $yMax} {
1770            set limits(ymax) $yMax
1771        }
1772        if {![info exists limits(zmin)] || $limits(zmin) > $zMin} {
1773            set limits(zmin) $zMin
1774        }
1775        if {![info exists limits(zmax)] || $limits(zmax) < $zMax} {
1776            set limits(zmax) $zMax
1777        }
1778    }
1779    return [array get limits]
1780}
1781
1782itcl::body Rappture::VtkStreamlinesViewer::BuildVolumeTab {} {
1783
1784    set fg [option get $itk_component(hull) font Font]
1785    #set bfg [option get $itk_component(hull) boldFont Font]
1786
1787    set inner [$itk_component(main) insert end \
1788        -title "Volume Settings" \
1789        -icon [Rappture::icon volume-on]]
1790    $inner configure -borderwidth 4
1791
1792    checkbutton $inner.volume \
1793        -text "Show Volume" \
1794        -variable [itcl::scope _settings(volumeVisible)] \
1795        -command [itcl::code $this AdjustSetting volumeVisible] \
1796        -font "Arial 9"
1797
1798    checkbutton $inner.wireframe \
1799        -text "Show Wireframe" \
1800        -variable [itcl::scope _settings(volumeWireframe)] \
1801        -command [itcl::code $this AdjustSetting volumeWireframe] \
1802        -font "Arial 9"
1803
1804    checkbutton $inner.lighting \
1805        -text "Enable Lighting" \
1806        -variable [itcl::scope _settings(volumeLighting)] \
1807        -command [itcl::code $this AdjustSetting volumeLighting] \
1808        -font "Arial 9"
1809
1810    checkbutton $inner.edges \
1811        -text "Show Edges" \
1812        -variable [itcl::scope _settings(volumeEdges)] \
1813        -command [itcl::code $this AdjustSetting volumeEdges] \
1814        -font "Arial 9"
1815
1816    label $inner.opacity_l -text "Opacity" -font "Arial 9"
1817    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1818        -variable [itcl::scope _settings(volumeOpacity)] \
1819        -width 10 \
1820        -showvalue off \
1821        -command [itcl::code $this AdjustSetting volumeOpacity]
1822
1823    blt::table $inner \
1824        0,0 $inner.wireframe -anchor w -pady 2 -cspan 2 \
1825        1,0 $inner.lighting  -anchor w -pady 2 -cspan 2 \
1826        2,0 $inner.edges     -anchor w -pady 2 -cspan 3 \
1827        3,0 $inner.opacity_l -anchor w -pady 2 \
1828        3,1 $inner.opacity   -fill x   -pady 2
1829
1830    blt::table configure $inner r* c* -resize none
1831    blt::table configure $inner r4 c1 -resize expand
1832}
1833
1834
1835itcl::body Rappture::VtkStreamlinesViewer::BuildStreamsTab {} {
1836
1837    set fg [option get $itk_component(hull) font Font]
1838    #set bfg [option get $itk_component(hull) boldFont Font]
1839
1840    set inner [$itk_component(main) insert end \
1841        -title "Streams Settings" \
1842        -icon [Rappture::icon streamlines-on]]
1843    $inner configure -borderwidth 4
1844
1845    checkbutton $inner.streamlines \
1846        -text "Show Streamlines" \
1847        -variable [itcl::scope _settings(streamlinesVisible)] \
1848        -command [itcl::code $this AdjustSetting streamlinesVisible] \
1849        -font "Arial 9"
1850   
1851    checkbutton $inner.lighting \
1852        -text "Enable Lighting" \
1853        -variable [itcl::scope _settings(streamlinesLighting)] \
1854        -command [itcl::code $this AdjustSetting streamlinesLighting] \
1855        -font "Arial 9"
1856
1857    checkbutton $inner.seeds \
1858        -text "Show Seeds" \
1859        -variable [itcl::scope _settings(streamlinesSeedsVisible)] \
1860        -command [itcl::code $this AdjustSetting streamlinesSeedsVisible] \
1861        -font "Arial 9"
1862
1863    label $inner.mode_l -text "Mode" -font "Arial 9"
1864    itk_component add streammode {
1865        Rappture::Combobox $inner.mode -width 10 -editable no
1866    }
1867    $inner.mode choices insert end \
1868        "lines"    "lines" \
1869        "ribbons"   "ribbons" \
1870        "tubes"     "tubes"
1871    $itk_component(streammode) value $_settings(streamlinesMode)
1872    bind $inner.mode <<Value>> [itcl::code $this AdjustSetting streamlinesMode]
1873
1874    label $inner.opacity_l -text "Opacity" -font "Arial 9"
1875    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1876        -variable [itcl::scope _settings(streamlinesOpacity)] \
1877        -width 10 \
1878        -showvalue off \
1879        -command [itcl::code $this AdjustSetting streamlinesOpacity]
1880
1881    label $inner.density_l -text "No. Seeds" -font "Arial 9"
1882    ::scale $inner.density -from 1 -to 1000 -orient horizontal \
1883        -variable [itcl::scope _settings(streamlinesNumSeeds)] \
1884        -width 10 \
1885        -showvalue on \
1886        -command [itcl::code $this AdjustSetting streamlinesNumSeeds]
1887
1888    label $inner.scale_l -text "Scale" -font "Arial 9"
1889    ::scale $inner.scale -from 1 -to 100 -orient horizontal \
1890        -variable [itcl::scope _settings(streamlinesScale)] \
1891        -width 10 \
1892        -showvalue off \
1893        -command [itcl::code $this AdjustSetting streamlinesScale]
1894
1895    label $inner.field_l -text "Field" -font "Arial 9"
1896    itk_component add field {
1897        Rappture::Combobox $inner.field -width 10 -editable no
1898    }
1899    bind $inner.field <<Value>> \
1900        [itcl::code $this AdjustSetting streamlines-field]
1901
1902    label $inner.palette_l -text "Palette" -font "Arial 9"
1903    itk_component add palette {
1904        Rappture::Combobox $inner.palette -width 10 -editable no
1905    }
1906    $inner.palette choices insert end \
1907        "BCGYR"              "BCGYR"            \
1908        "BGYOR"              "BGYOR"            \
1909        "blue"               "blue"             \
1910        "blue-to-brown"      "blue-to-brown"    \
1911        "blue-to-orange"     "blue-to-orange"   \
1912        "blue-to-grey"       "blue-to-grey"     \
1913        "green-to-magenta"   "green-to-magenta" \
1914        "greyscale"          "greyscale"        \
1915        "nanohub"            "nanohub"          \
1916        "rainbow"            "rainbow"          \
1917        "spectral"           "spectral"         \
1918        "ROYGB"              "ROYGB"            \
1919        "RYGCB"              "RYGCB"            \
1920        "brown-to-blue"      "brown-to-blue"    \
1921        "grey-to-blue"       "grey-to-blue"     \
1922        "orange-to-blue"     "orange-to-blue"   
1923
1924    $itk_component(palette) value "BCGYR"
1925    bind $inner.palette <<Value>> \
1926        [itcl::code $this AdjustSetting streamlines-palette]
1927
1928    blt::table $inner \
1929        0,0 $inner.palette_l   -anchor w -pady 2  \
1930        0,1 $inner.palette     -fill x   -pady 2  \
1931        1,0 $inner.field_l     -anchor w -pady 2  \
1932        1,1 $inner.field       -fill x   -pady 2  \
1933        2,0 $inner.mode_l      -anchor w -pady 2  \
1934        2,1 $inner.mode        -fill x   -pady 2  \
1935        3,0 $inner.opacity_l   -anchor w -pady 2  \
1936        3,1 $inner.opacity     -anchor w -pady 2  \
1937        5,0 $inner.lighting    -anchor w -pady 2 -cspan 2 \
1938        6,0 $inner.seeds       -anchor w -pady 2 -cspan 2 \
1939        7,0 $inner.density_l   -anchor w -pady 2  \
1940        7,1 $inner.density     -fill x   -pady 2  \
1941
1942    blt::table configure $inner r* c* -resize none
1943    blt::table configure $inner r10 c1 c2 -resize expand
1944}
1945
1946itcl::body Rappture::VtkStreamlinesViewer::BuildAxisTab {} {
1947
1948    set fg [option get $itk_component(hull) font Font]
1949    #set bfg [option get $itk_component(hull) boldFont Font]
1950
1951    set inner [$itk_component(main) insert end \
1952        -title "Axis Settings" \
1953        -icon [Rappture::icon axis1]]
1954    $inner configure -borderwidth 4
1955
1956    checkbutton $inner.visible \
1957        -text "Show Axes" \
1958        -variable [itcl::scope _settings(axesVisible)] \
1959        -command [itcl::code $this AdjustSetting axesVisible] \
1960        -font "Arial 9"
1961
1962    checkbutton $inner.labels \
1963        -text "Show Axis Labels" \
1964        -variable [itcl::scope _settings(axisLabelsVisible)] \
1965        -command [itcl::code $this AdjustSetting axisLabelsVisible] \
1966        -font "Arial 9"
1967
1968    checkbutton $inner.xgrid \
1969        -text "Show X Grid" \
1970        -variable [itcl::scope _settings(axisXGrid)] \
1971        -command [itcl::code $this AdjustSetting axisXGrid] \
1972        -font "Arial 9"
1973    checkbutton $inner.ygrid \
1974        -text "Show Y Grid" \
1975        -variable [itcl::scope _settings(axisYGrid)] \
1976        -command [itcl::code $this AdjustSetting axisYGrid] \
1977        -font "Arial 9"
1978    checkbutton $inner.zgrid \
1979        -text "Show Z Grid" \
1980        -variable [itcl::scope _settings(axisZGrid)] \
1981        -command [itcl::code $this AdjustSetting axisZGrid] \
1982        -font "Arial 9"
1983
1984    label $inner.mode_l -text "Mode" -font "Arial 9"
1985
1986    itk_component add axismode {
1987        Rappture::Combobox $inner.mode -width 10 -editable no
1988    }
1989    $inner.mode choices insert end \
1990        "static_triad"    "static" \
1991        "closest_triad"   "closest" \
1992        "furthest_triad"  "furthest" \
1993        "outer_edges"     "outer"         
1994    $itk_component(axismode) value "static"
1995    bind $inner.mode <<Value>> [itcl::code $this AdjustSetting axis-mode]
1996
1997    blt::table $inner \
1998        0,0 $inner.visible -anchor w -cspan 2 \
1999        1,0 $inner.labels  -anchor w -cspan 2 \
2000        2,0 $inner.xgrid   -anchor w -cspan 2 \
2001        3,0 $inner.ygrid   -anchor w -cspan 2 \
2002        4,0 $inner.zgrid   -anchor w -cspan 2 \
2003        5,0 $inner.mode_l  -anchor w -cspan 2 -padx { 2 0 } \
2004        6,0 $inner.mode    -fill x   -cspan 2
2005
2006    blt::table configure $inner r* c* -resize none
2007    blt::table configure $inner r7 c1 -resize expand
2008}
2009
2010
2011itcl::body Rappture::VtkStreamlinesViewer::BuildCameraTab {} {
2012    set inner [$itk_component(main) insert end \
2013        -title "Camera Settings" \
2014        -icon [Rappture::icon camera]]
2015    $inner configure -borderwidth 4
2016
2017    set labels { qx qy qz qw xpan ypan zoom }
2018    set row 0
2019    foreach tag $labels {
2020        label $inner.${tag}label -text $tag -font "Arial 9"
2021        entry $inner.${tag} -font "Arial 9"  -bg white \
2022            -textvariable [itcl::scope _view($tag)]
2023        bind $inner.${tag} <KeyPress-Return> \
2024            [itcl::code $this camera set ${tag}]
2025        blt::table $inner \
2026            $row,0 $inner.${tag}label -anchor e -pady 2 \
2027            $row,1 $inner.${tag} -anchor w -pady 2
2028        blt::table configure $inner r$row -resize none
2029        incr row
2030    }
2031    checkbutton $inner.ortho \
2032        -text "Orthographic Projection" \
2033        -variable [itcl::scope _view(ortho)] \
2034        -command [itcl::code $this camera set ortho] \
2035        -font "Arial 9"
2036    blt::table $inner \
2037            $row,0 $inner.ortho -columnspan 2 -anchor w -pady 2
2038    blt::table configure $inner r$row -resize none
2039    incr row
2040
2041    blt::table configure $inner c0 c1 -resize none
2042    blt::table configure $inner c2 -resize expand
2043    blt::table configure $inner r$row -resize expand
2044}
2045
2046itcl::body Rappture::VtkStreamlinesViewer::BuildCutplaneTab {} {
2047
2048    set fg [option get $itk_component(hull) font Font]
2049   
2050    set inner [$itk_component(main) insert end \
2051        -title "Cutplane Settings" \
2052        -icon [Rappture::icon cutbutton]]
2053
2054    $inner configure -borderwidth 4
2055
2056    checkbutton $inner.visible \
2057        -text "Show Cutplanes" \
2058        -variable [itcl::scope _settings(cutplaneVisible)] \
2059        -command [itcl::code $this AdjustSetting cutplaneVisible] \
2060        -font "Arial 9"
2061
2062    checkbutton $inner.wireframe \
2063        -text "Show Wireframe" \
2064        -variable [itcl::scope _settings(cutplaneWireframe)] \
2065        -command [itcl::code $this AdjustSetting cutplaneWireframe] \
2066        -font "Arial 9"
2067
2068    checkbutton $inner.lighting \
2069        -text "Enable Lighting" \
2070        -variable [itcl::scope _settings(cutplaneLighting)] \
2071        -command [itcl::code $this AdjustSetting cutplaneLighting] \
2072        -font "Arial 9"
2073
2074    checkbutton $inner.edges \
2075        -text "Show Edges" \
2076        -variable [itcl::scope _settings(cutplaneEdges)] \
2077        -command [itcl::code $this AdjustSetting cutplaneEdges] \
2078        -font "Arial 9"
2079
2080    label $inner.opacity_l -text "Opacity" -font "Arial 9"
2081    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
2082        -variable [itcl::scope _settings(cutplaneOpacity)] \
2083        -width 10 \
2084        -showvalue off \
2085        -command [itcl::code $this AdjustSetting cutplaneOpacity]
2086    $inner.opacity set $_settings(cutplaneOpacity)
2087
2088    # X-value slicer...
2089    itk_component add xCutButton {
2090        Rappture::PushButton $inner.xbutton \
2091            -onimage [Rappture::icon x-cutplane-red] \
2092            -offimage [Rappture::icon x-cutplane-red] \
2093            -command [itcl::code $this AdjustSetting cutplaneXVisible] \
2094            -variable [itcl::scope _settings(cutplaneXVisible)]
2095    }
2096    Rappture::Tooltip::for $itk_component(xCutButton) \
2097        "Toggle the X-axis cutplane on/off"
2098    $itk_component(xCutButton) select
2099
2100    itk_component add xCutScale {
2101        ::scale $inner.xval -from 100 -to 0 \
2102            -width 10 -orient vertical -showvalue yes \
2103            -borderwidth 1 -highlightthickness 0 \
2104            -command [itcl::code $this EventuallySetCutplane x] \
2105            -variable [itcl::scope _settings(cutplaneXPosition)] \
2106            -foreground red3 -font "Arial 9 bold"
2107    } {
2108        usual
2109        ignore -borderwidth -highlightthickness -foreground -font
2110    }
2111    # Set the default cutplane value before disabling the scale.
2112    $itk_component(xCutScale) set 50
2113    $itk_component(xCutScale) configure -state disabled
2114    Rappture::Tooltip::for $itk_component(xCutScale) \
2115        "@[itcl::code $this Slice tooltip x]"
2116
2117    # Y-value slicer...
2118    itk_component add yCutButton {
2119        Rappture::PushButton $inner.ybutton \
2120            -onimage [Rappture::icon y-cutplane-green] \
2121            -offimage [Rappture::icon y-cutplane-green] \
2122            -command [itcl::code $this AdjustSetting cutplaneYVisible] \
2123            -variable [itcl::scope _settings(cutplaneYVisible)]
2124    }
2125    Rappture::Tooltip::for $itk_component(yCutButton) \
2126        "Toggle the Y-axis cutplane on/off"
2127    $itk_component(yCutButton) select
2128
2129    itk_component add yCutScale {
2130        ::scale $inner.yval -from 100 -to 0 \
2131            -width 10 -orient vertical -showvalue yes \
2132            -borderwidth 1 -highlightthickness 0 \
2133            -command [itcl::code $this EventuallySetCutplane y] \
2134            -variable [itcl::scope _settings(cutplaneYPosition)] \
2135            -foreground green3 -font "Arial 9 bold"
2136    } {
2137        usual
2138        ignore -borderwidth -highlightthickness -foreground -font
2139    }
2140    Rappture::Tooltip::for $itk_component(yCutScale) \
2141        "@[itcl::code $this Slice tooltip y]"
2142    # Set the default cutplane value before disabling the scale.
2143    $itk_component(yCutScale) set 50
2144    $itk_component(yCutScale) configure -state disabled
2145
2146    # Z-value slicer...
2147    itk_component add zCutButton {
2148        Rappture::PushButton $inner.zbutton \
2149            -onimage [Rappture::icon z-cutplane-blue] \
2150            -offimage [Rappture::icon z-cutplane-blue] \
2151            -command [itcl::code $this AdjustSetting cutplaneZVisible] \
2152            -variable [itcl::scope _settings(cutplaneZVisible)]
2153    }
2154    Rappture::Tooltip::for $itk_component(zCutButton) \
2155        "Toggle the Z-axis cutplane on/off"
2156    $itk_component(zCutButton) select
2157
2158    itk_component add zCutScale {
2159        ::scale $inner.zval -from 100 -to 0 \
2160            -width 10 -orient vertical -showvalue yes \
2161            -borderwidth 1 -highlightthickness 0 \
2162            -command [itcl::code $this EventuallySetCutplane z] \
2163            -variable [itcl::scope _settings(cutplaneZPosition)] \
2164            -foreground blue3 -font "Arial 9 bold"
2165    } {
2166        usual
2167        ignore -borderwidth -highlightthickness -foreground -font
2168    }
2169    $itk_component(zCutScale) set 50
2170    $itk_component(zCutScale) configure -state disabled
2171    #$itk_component(zCutScale) configure -state disabled
2172    Rappture::Tooltip::for $itk_component(zCutScale) \
2173        "@[itcl::code $this Slice tooltip z]"
2174
2175    blt::table $inner \
2176        0,0 $inner.lighting             -anchor w -pady 2 -cspan 4 \
2177        1,0 $inner.wireframe            -anchor w -pady 2 -cspan 4 \
2178        2,0 $inner.edges                -anchor w -pady 2 -cspan 4 \
2179        3,0 $inner.opacity_l            -anchor w -pady 2 -cspan 1 \
2180        3,1 $inner.opacity              -fill x   -pady 2 -cspan 3 \
2181        4,0 $itk_component(xCutButton)  -anchor w -padx 2 -pady 2 \
2182        5,0 $itk_component(yCutButton)  -anchor w -padx 2 -pady 2 \
2183        6,0 $itk_component(zCutButton)  -anchor w -padx 2 -pady 2 \
2184        4,1 $itk_component(xCutScale)   -fill y -rspan 4 \
2185        4,2 $itk_component(yCutScale)   -fill y -rspan 4 \
2186        4,3 $itk_component(zCutScale)   -fill y -rspan 4 \
2187
2188    blt::table configure $inner r* c* -resize none
2189    blt::table configure $inner r7 c4 -resize expand
2190}
2191
2192
2193
2194#
2195#  camera --
2196#
2197itcl::body Rappture::VtkStreamlinesViewer::camera {option args} {
2198    switch -- $option {
2199        "show" {
2200            puts [array get _view]
2201        }
2202        "set" {
2203            set who [lindex $args 0]
2204            set x $_view($who)
2205            set code [catch { string is double $x } result]
2206            if { $code != 0 || !$result } {
2207                return
2208            }
2209            switch -- $who {
2210                "ortho" {
2211                    if {$_view(ortho)} {
2212                        SendCmd "camera mode ortho"
2213                    } else {
2214                        SendCmd "camera mode persp"
2215                    }
2216                }
2217                "xpan" - "ypan" {
2218                    PanCamera
2219                }
2220                "qx" - "qy" - "qz" - "qw" {
2221                    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
2222                    $_arcball quaternion $q
2223                    EventuallyRotate $q
2224                }
2225                "zoom" {
2226                    SendCmd "camera zoom $_view(zoom)"
2227                }
2228            }
2229        }
2230    }
2231}
2232
2233itcl::body Rappture::VtkStreamlinesViewer::ConvertToVtkData { dataobj comp } {
2234    foreach { x1 x2 xN y1 y2 yN } [$dataobj mesh $comp] break
2235    set values [$dataobj values $comp]
2236    append out "# vtk DataFile Version 2.0 \n"
2237    append out "Test data \n"
2238    append out "ASCII \n"
2239    append out "DATASET STRUCTURED_POINTS \n"
2240    append out "DIMENSIONS $xN $yN 1 \n"
2241    append out "ORIGIN 0 0 0 \n"
2242    append out "SPACING 1 1 1 \n"
2243    append out "POINT_DATA [expr $xN * $yN] \n"
2244    append out "SCALARS field float 1 \n"
2245    append out "LOOKUP_TABLE default \n"
2246    append out [join $values "\n"]
2247    append out "\n"
2248    return $out
2249}
2250
2251
2252itcl::body Rappture::VtkStreamlinesViewer::GetVtkData { args } {
2253    set bytes ""
2254    foreach dataobj [get] {
2255        foreach comp [$dataobj components] {
2256            set tag $dataobj-$comp
2257            #set contents [ConvertToVtkData $dataobj $comp]
2258            set contents [$dataobj blob $comp]
2259            append bytes "$contents\n\n"
2260        }
2261    }
2262    return [list .vtk $bytes]
2263}
2264
2265itcl::body Rappture::VtkStreamlinesViewer::GetImage { args } {
2266    if { [image width $_image(download)] > 0 &&
2267         [image height $_image(download)] > 0 } {
2268        set bytes [$_image(download) data -format "jpeg -quality 100"]
2269        set bytes [Rappture::encoding::decode -as b64 $bytes]
2270        return [list .jpg $bytes]
2271    }
2272    return ""
2273}
2274
2275itcl::body Rappture::VtkStreamlinesViewer::BuildDownloadPopup { popup command } {
2276    Rappture::Balloon $popup \
2277        -title "[Rappture::filexfer::label downloadWord] as..."
2278    set inner [$popup component inner]
2279    label $inner.summary -text "" -anchor w
2280    radiobutton $inner.vtk_button -text "VTK data file" \
2281        -variable [itcl::scope _downloadPopup(format)] \
2282        -font "Helvetica 9 " \
2283        -value vtk 
2284    Rappture::Tooltip::for $inner.vtk_button "Save as VTK data file."
2285    radiobutton $inner.image_button -text "Image File" \
2286        -variable [itcl::scope _downloadPopup(format)] \
2287        -value image
2288    Rappture::Tooltip::for $inner.image_button \
2289        "Save as digital image."
2290
2291    button $inner.ok -text "Save" \
2292        -highlightthickness 0 -pady 2 -padx 3 \
2293        -command $command \
2294        -compound left \
2295        -image [Rappture::icon download]
2296
2297    button $inner.cancel -text "Cancel" \
2298        -highlightthickness 0 -pady 2 -padx 3 \
2299        -command [list $popup deactivate] \
2300        -compound left \
2301        -image [Rappture::icon cancel]
2302
2303    blt::table $inner \
2304        0,0 $inner.summary -cspan 2  \
2305        1,0 $inner.vtk_button -anchor w -cspan 2 -padx { 4 0 } \
2306        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
2307        4,1 $inner.cancel -width .9i -fill y \
2308        4,0 $inner.ok -padx 2 -width .9i -fill y
2309    blt::table configure $inner r3 -height 4
2310    blt::table configure $inner r4 -pady 4
2311    raise $inner.image_button
2312    $inner.vtk_button invoke
2313    return $inner
2314}
2315
2316itcl::body Rappture::VtkStreamlinesViewer::SetObjectStyle { dataobj comp } {
2317    # Parse style string.
2318    set tag $dataobj-$comp
2319    set style [$dataobj style $comp]
2320    array set settings {
2321        -color \#808080
2322        -edges 0
2323        -edgecolor black
2324        -linewidth 1.0
2325        -opacity 0.4
2326        -wireframe 0
2327        -lighting 1
2328        -seeds 1
2329        -seedcolor white
2330        -visible 1
2331    }
2332    if { $dataobj != $_first } {
2333        set settings(-opacity) 1
2334    }
2335    array set settings $style
2336    SendCmd "streamlines add $tag"
2337    SendCmd "streamlines seed visible off $tag"
2338    set seeds [$dataobj hints seeds]
2339    if { $seeds != "" && ![info exists _seeds($dataobj)] } {
2340        set length [string length $seeds]
2341        SendCmd "streamlines seed fmesh 200 data follows $length $tag"
2342        SendCmd "$seeds"
2343        set _seeds($dataobj) 1
2344    }
2345    SendCmd "cutplane add $tag"
2346    SendCmd "cutplane edges 0 $tag"
2347    SendCmd "cutplane wireframe 0 $tag"
2348    SendCmd "cutplane lighting 1 $tag"
2349    SendCmd "cutplane linewidth 1 $tag"
2350    #SendCmd "cutplane linecolor 1 1 1 $tag"
2351    #SendCmd "cutplane visible $tag"
2352    foreach axis { x y z } {
2353        SendCmd "cutplane slice $axis 0.5 $tag"
2354        SendCmd "cutplane axis $axis 0 $tag"
2355    }
2356
2357    SendCmd "polydata add $tag"
2358    SendCmd "polydata edges $settings(-edges) $tag"
2359    set _settings(volumeEdges) $settings(-edges)
2360    SendCmd "polydata color [Color2RGB $settings(-color)] $tag"
2361    SendCmd "polydata lighting $settings(-lighting) $tag"
2362    set _settings(volumeLighting) $settings(-lighting)
2363    SendCmd "polydata linecolor [Color2RGB $settings(-edgecolor)] $tag"
2364    SendCmd "polydata linewidth $settings(-linewidth) $tag"
2365    SendCmd "polydata opacity $settings(-opacity) $tag"
2366    set _settings(volumeOpacity) $settings(-opacity)
2367    SendCmd "polydata wireframe $settings(-wireframe) $tag"
2368    set _settings(volumeWireframe) $settings(-wireframe)
2369    set _settings(volumeOpacity) [expr $settings(-opacity) * 100.0]
2370    SetColormap $dataobj $comp
2371}
2372
2373itcl::body Rappture::VtkStreamlinesViewer::IsValidObject { dataobj } {
2374    if {[catch {$dataobj isa Rappture::Field} valid] != 0 || !$valid} {
2375        return 0
2376    }
2377    return 1
2378}
2379
2380# ----------------------------------------------------------------------
2381# USAGE: ReceiveLegend <colormap> <title> <vmin> <vmax> <size>
2382#
2383# Invoked automatically whenever the "legend" command comes in from
2384# the rendering server.  Indicates that binary image data with the
2385# specified <size> will follow.
2386# ----------------------------------------------------------------------
2387itcl::body Rappture::VtkStreamlinesViewer::ReceiveLegend { colormap title vmin vmax size } {
2388    set _legendPending 0
2389    puts stderr "ReceiveLegend colormap=$colormap title=$title range=$vmin,$vmax size=$size"
2390    set _limits(vmin) $vmin
2391    set _limits(vmax) $vmax
2392    set _title $title
2393    regsub {\(mag\)} $title "" _title
2394    if { [IsConnected] } {
2395        set bytes [ReceiveBytes $size]
2396        if { ![info exists _image(legend)] } {
2397            set _image(legend) [image create photo]
2398        }
2399        $_image(legend) configure -data $bytes
2400        #puts stderr "read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
2401        if { [catch {DrawLegend $_title} errs] != 0 } {
2402            puts stderr errs=$errs
2403        }
2404    }
2405}
2406
2407#
2408# DrawLegend --
2409#
2410#       Draws the legend in it's own canvas which resides to the right
2411#       of the contour plot area.
2412#
2413itcl::body Rappture::VtkStreamlinesViewer::DrawLegend { name } {
2414    set c $itk_component(view)
2415    set w [winfo width $c]
2416    set h [winfo height $c]
2417    set font "Arial 8"
2418    set lineht [font metrics $font -linespace]
2419   
2420    if { [info exists _fields($name)] } {
2421        foreach { title units } $_fields($name) break
2422        if { $units != "" } {
2423            set title [format "%s (%s)" $title $units]
2424        }
2425    } else {
2426        set title $name
2427    }
2428    if { $_settings(legendVisible) } {
2429        set x [expr $w - 2]
2430        if { [$c find withtag "legend"] == "" } {
2431            set y 2
2432            $c create text $x $y \
2433                -anchor ne \
2434                -fill $itk_option(-plotforeground) -tags "title legend" \
2435                -font $font
2436            incr y $lineht
2437            $c create text $x $y \
2438                -anchor ne \
2439                -fill $itk_option(-plotforeground) -tags "vmax legend" \
2440                -font $font
2441            incr y $lineht
2442            $c create image $x $y \
2443                -anchor ne \
2444                -image $_image(legend) -tags "colormap legend"
2445            $c create text $x [expr {$h-2}] \
2446                -anchor se \
2447                -fill $itk_option(-plotforeground) -tags "vmin legend" \
2448                -font $font
2449            #$c bind colormap <Enter> [itcl::code $this EnterLegend %x %y]
2450            $c bind colormap <Leave> [itcl::code $this LeaveLegend]
2451            $c bind colormap <Motion> [itcl::code $this MotionLegend %x %y]
2452        }
2453        $c bind title <ButtonPress> [itcl::code $this Combo post]
2454        $c bind title <Enter> [itcl::code $this Combo activate]
2455        $c bind title <Leave> [itcl::code $this Combo deactivate]
2456        # Reset the item coordinates according the current size of the plot.
2457        $c itemconfigure title -text $title
2458        if { $_limits(vmin) != "" } {
2459            $c itemconfigure vmin -text [format %g $_limits(vmin)]
2460        }
2461        if { $_limits(vmax) != "" } {
2462            $c itemconfigure vmax -text [format %g $_limits(vmax)]
2463        }
2464        set y 2
2465        $c coords title $x $y
2466        incr y $lineht
2467        $c coords vmax $x $y
2468        incr y $lineht
2469        $c coords colormap $x $y
2470        $c coords vmin $x [expr {$h - 2}]
2471    }
2472}
2473
2474#
2475# EnterLegend --
2476#
2477itcl::body Rappture::VtkStreamlinesViewer::EnterLegend { x y } {
2478    SetLegendTip $x $y
2479}
2480
2481#
2482# MotionLegend --
2483#
2484itcl::body Rappture::VtkStreamlinesViewer::MotionLegend { x y } {
2485    Rappture::Tooltip::tooltip cancel
2486    set c $itk_component(view)
2487    SetLegendTip $x $y
2488}
2489
2490#
2491# LeaveLegend --
2492#
2493itcl::body Rappture::VtkStreamlinesViewer::LeaveLegend { } {
2494    Rappture::Tooltip::tooltip cancel
2495    .rappturetooltip configure -icon ""
2496}
2497
2498#
2499# SetLegendTip --
2500#
2501itcl::body Rappture::VtkStreamlinesViewer::SetLegendTip { x y } {
2502    set c $itk_component(view)
2503    set w [winfo width $c]
2504    set h [winfo height $c]
2505    set font "Arial 8"
2506    set lineht [font metrics $font -linespace]
2507   
2508    set imgHeight [image height $_image(legend)]
2509    set coords [$c coords colormap]
2510    set imgX [expr $w - [image width $_image(legend)] - 2]
2511    set imgY [expr $y - 2 * ($lineht + 2)]
2512
2513    if { [info exists _fields($_title)] } {
2514        foreach { title units } $_fields($_title) break
2515        if { $units != "" } {
2516            set title [format "%s (%s)" $title $units]
2517        }
2518    } else {
2519        set title $_title
2520    }
2521    # Make a swatch of the selected color
2522    if { [catch { $_image(legend) get 10 $imgY } pixel] != 0 } {
2523        #puts stderr "out of range: $imgY"
2524        return
2525    }
2526    if { ![info exists _image(swatch)] } {
2527        set _image(swatch) [image create photo -width 24 -height 24]
2528    }
2529    set color [eval format "\#%02x%02x%02x" $pixel]
2530    $_image(swatch) put black  -to 0 0 23 23
2531    $_image(swatch) put $color -to 1 1 22 22
2532    .rappturetooltip configure -icon $_image(swatch)
2533
2534    # Compute the value of the point
2535    if { [info exists _limits(vmax)] && [info exists _limits(vmin)] } {
2536        set t [expr 1.0 - (double($imgY) / double($imgHeight-1))]
2537        set value [expr $t * ($_limits(vmax) - $_limits(vmin)) + $_limits(vmin)]
2538    } else {
2539        set value 0.0
2540    }
2541    set tipx [expr $x + 15]
2542    set tipy [expr $y - 5]
2543    Rappture::Tooltip::text $c "$title $value"
2544    Rappture::Tooltip::tooltip show $c +$tipx,+$tipy   
2545}
2546
2547
2548# ----------------------------------------------------------------------
2549# USAGE: Slice move x|y|z <newval>
2550#
2551# Called automatically when the user drags the slider to move the
2552# cut plane that slices 3D data.  Gets the current value from the
2553# slider and moves the cut plane to the appropriate point in the
2554# data set.
2555# ----------------------------------------------------------------------
2556itcl::body Rappture::VtkStreamlinesViewer::Slice {option args} {
2557    switch -- $option {
2558        "move" {
2559            set axis [lindex $args 0]
2560            set oldval $_settings(axis-${axis}position)
2561            set newval [lindex $args 1]
2562            if {[llength $args] != 2} {
2563                error "wrong # args: should be \"Slice move x|y|z newval\""
2564            }
2565            set newpos [expr {0.01*$newval}]
2566            SendCmd "cutplane slice $axis $newpos"
2567        }
2568        "tooltip" {
2569            set axis [lindex $args 0]
2570            set val [$itk_component(${axis}CutScale) get]
2571            return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
2572        }
2573        default {
2574            error "bad option \"$option\": should be axis, move, or tooltip"
2575        }
2576    }
2577}
2578
2579
2580# ----------------------------------------------------------------------
2581# USAGE: _dropdown post
2582# USAGE: _dropdown unpost
2583# USAGE: _dropdown select
2584#
2585# Used internally to handle the dropdown list for this combobox.  The
2586# post/unpost options are invoked when the list is posted or unposted
2587# to manage the relief of the controlling button.  The select option
2588# is invoked whenever there is a selection from the list, to assign
2589# the value back to the gauge.
2590# ----------------------------------------------------------------------
2591itcl::body Rappture::VtkStreamlinesViewer::Combo {option} {
2592    set c $itk_component(view)
2593    switch -- $option {
2594        post {
2595            foreach { x1 y1 x2 y2 } [$c bbox title] break
2596            set x1 [expr [winfo width $itk_component(view)] - [winfo reqwidth $itk_component(fieldmenu)]]
2597            set x [expr $x1 + [winfo rootx $itk_component(view)]]
2598            set y [expr $y2 + [winfo rooty $itk_component(view)]]
2599            puts stderr "combo x=$x y=$y"
2600            tk_popup $itk_component(fieldmenu) $x $y
2601        }
2602        activate {
2603            $c itemconfigure title -fill red
2604        }
2605        deactivate {
2606            $c itemconfigure title -fill white
2607        }
2608        invoke {
2609            $itk_component(field) value $_currentField
2610            AdjustSetting streamlines-field
2611        }
2612        default {
2613            error "bad option \"$option\": should be post, unpost, select"
2614        }
2615    }
2616}
Note: See TracBrowser for help on using the repository browser.