source: trunk/gui/scripts/nanovisviewer.tcl @ 3093

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

Fix widget names, canonical view orientations in BuildCameraTab?. Note that
the drop down is not yet added to the layout.

File size: 71.4 KB
Line 
1
2# ----------------------------------------------------------------------
3#  COMPONENT: nanovisviewer - 3D volume rendering
4#
5#  This widget performs volume rendering on 3D scalar/vector datasets.
6#  It connects to the Nanovis server running on a rendering farm,
7#  transmits data, and displays the results.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2005  Purdue Research Foundation
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require BLT
17package require Img
18                                       
19#
20# FIXME:
21#       Need to Add DX readers this client to examine the data before
22#       it's sent to the server.  This will eliminate 90% of the insanity in
23#       computing the limits of all the volumes.  I can rip out all the
24#       "receive data" "send transfer function" event crap.
25#
26#       This means we can compute the transfer function (relative values) and
27#       draw the legend min/max values without waiting for the information to
28#       come from the server.  This will also prevent the flashing that occurs
29#       when a new volume is drawn (using the default transfer function) and
30#       then when the correct transfer function has been sent and linked to
31#       the volume. 
32#
33option add *NanovisViewer.width 4i widgetDefault
34option add *NanovisViewer*cursor crosshair widgetDefault
35option add *NanovisViewer.height 4i widgetDefault
36option add *NanovisViewer.foreground black widgetDefault
37option add *NanovisViewer.controlBackground gray widgetDefault
38option add *NanovisViewer.controlDarkBackground #999999 widgetDefault
39option add *NanovisViewer.plotBackground black widgetDefault
40option add *NanovisViewer.plotForeground white widgetDefault
41option add *NanovisViewer.plotOutline gray widgetDefault
42option add *NanovisViewer.font \
43    -*-helvetica-medium-r-normal-*-12-* widgetDefault
44
45# must use this name -- plugs into Rappture::resources::load
46proc NanovisViewer_init_resources {} {
47    Rappture::resources::register \
48        nanovis_server Rappture::NanovisViewer::SetServerList
49}
50
51itcl::class Rappture::NanovisViewer {
52    inherit Rappture::VisViewer
53
54    itk_option define -plotforeground plotForeground Foreground ""
55    itk_option define -plotbackground plotBackground Background ""
56    itk_option define -plotoutline plotOutline PlotOutline ""
57
58    constructor { hostlist args } {
59        Rappture::VisViewer::constructor $hostlist
60    } {
61        # defined below
62    }
63    destructor {
64        # defined below
65    }
66    public proc SetServerList { namelist } {
67        Rappture::VisViewer::SetServerList "nanovis" $namelist
68    }
69    public method add {dataobj {settings ""}}
70    public method camera {option args}
71    public method delete {args}
72    public method disconnect {}
73    public method download {option args}
74    public method get {args}
75    public method isconnected {}
76    public method limits { tf }
77    public method overmarker { m x }
78    public method sendto { string }
79    public method parameters {title args} {
80        # do nothing
81    }
82    public method rmdupmarker { m x }
83    public method scale {args}
84    public method updatetransferfuncs {}
85
86    protected method Connect {}
87    protected method CurrentVolumes {{what -all}}
88    protected method Disconnect {}
89    protected method DoResize {}
90    protected method FixLegend {}
91    protected method FixSettings {what {value ""}}
92    protected method Pan {option x y}
93    protected method Rebuild {}
94    protected method ReceiveData { args }
95    protected method ReceiveImage { args }
96    protected method ReceiveLegend { tf vmin vmax size }
97    protected method Rotate {option x y}
98    protected method SendCmd {string}
99    protected method SendTransferFuncs {}
100    protected method Slice {option args}
101    protected method SlicerTip {axis}
102    protected method Zoom {option}
103
104    # The following methods are only used by this class.
105    private method AddIsoMarker { x y }
106    private method BuildCameraTab {}
107    private method BuildCutplanesTab {}
108    private method BuildViewTab {}
109    private method BuildVolumeTab {}
110    private method ComputeTransferFunc { tf }
111    private method EventuallyResize { w h }
112    private method EventuallyResizeLegend { }
113    private method NameTransferFunc { dataobj comp }
114    private method PanCamera {}
115    private method ParseLevelsOption { tf levels }
116    private method ParseMarkersOption { tf markers }
117    private method volume { tag name }
118    private method GetVolumeInfo { w }
119    private method SetOrientation {}
120
121    private variable _outbuf       ;# buffer for outgoing commands
122
123    private variable _dlist ""     ;# list of data objects
124    private variable _allDataObjs
125    private variable _obj2ovride   ;# maps dataobj => style override
126    private variable _serverVols   ;# contains all the dataobj-component
127                                   ;# to volumes in the server
128    private variable _serverTfs    ;# contains all the transfer functions
129                                   ;# in the server.
130    private variable _recvdVols    ;# list of data objs to send to server
131    private variable _vol2style    ;# maps dataobj-component to transfunc
132    private variable _style2vols   ;# maps tf back to list of
133                                    # dataobj-components using the tf.
134
135    private variable _click        ;# info used for rotate operations
136    private variable _limits       ;# autoscale min/max for all axes
137    private variable _view         ;# view params for 3D view
138    private variable _isomarkers    ;# array of isosurface level values 0..1
139    private common   _settings
140    # Array of transfer functions in server.  If 0 the transfer has been
141    # defined but not loaded.  If 1 the transfer function has been named
142    # and loaded.
143    private variable _activeTfs
144    private variable _first ""     ;# This is the topmost volume.
145    private variable _buffering 0
146
147    # This
148    # indicates which isomarkers and transfer
149    # function to use when changing markers,
150    # opacity, or thickness.
151    common _downloadPopup          ;# download options from popup
152    private common _hardcopy
153    private variable _width 0
154    private variable _height 0
155    private variable _resizePending 0
156    private variable _resizeLegendPending 0
157}
158
159itk::usual NanovisViewer {
160    keep -background -foreground -cursor -font
161    keep -plotbackground -plotforeground
162}
163
164# ----------------------------------------------------------------------
165# CONSTRUCTOR
166# ----------------------------------------------------------------------
167itcl::body Rappture::NanovisViewer::constructor {hostlist args} {
168    set _serverType "nanovis"
169
170    # Draw legend event
171    $_dispatcher register !legend
172    $_dispatcher dispatch $this !legend "[itcl::code $this FixLegend]; list"
173
174    # Send transfer functions event
175    $_dispatcher register !send_transfunc
176    $_dispatcher dispatch $this !send_transfunc \
177        "[itcl::code $this SendTransferFuncs]; list"
178
179    # Rebuild event
180    $_dispatcher register !rebuild
181    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
182
183    # Resize event
184    $_dispatcher register !resize
185    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
186
187    set _outbuf ""
188
189    #
190    # Populate parser with commands handle incoming requests
191    #
192    $_parser alias image [itcl::code $this ReceiveImage]
193    $_parser alias legend [itcl::code $this ReceiveLegend]
194    $_parser alias data [itcl::code $this ReceiveData]
195
196    # Initialize the view to some default parameters.
197    array set _view {
198        theta   45
199        phi     45
200        psi     0
201        zoom    1.0
202        pan-x   0
203        pan-y   0
204    }
205    set _limits(vmin) 0.0
206    set _limits(vmax) 1.0
207
208    array set _settings [subst {
209        $this-pan-x             $_view(pan-x)
210        $this-pan-y             $_view(pan-y)
211        $this-phi               $_view(phi)
212        $this-psi               $_view(psi)
213        $this-theta             $_view(theta)
214        $this-volume            1
215        $this-xcutplane         0
216        $this-xcutposition      0
217        $this-ycutplane         0
218        $this-ycutposition      0
219        $this-zcutplane         0
220        $this-zcutposition      0
221        $this-zoom              $_view(zoom)
222    }]
223
224    itk_component add 3dview {
225        label $itk_component(plotarea).view -image $_image(plot) \
226            -highlightthickness 0 -borderwidth 0
227    } {
228        usual
229        ignore -highlightthickness -borderwidth  -background
230    }
231
232    set f [$itk_component(main) component controls]
233    itk_component add reset {
234        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
235            -highlightthickness 0 \
236            -image [Rappture::icon reset-view] \
237            -command [itcl::code $this Zoom reset]
238    } {
239        usual
240        ignore -highlightthickness
241    }
242    pack $itk_component(reset) -side top -padx 2 -pady 2
243    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
244
245    itk_component add zoomin {
246        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
247            -highlightthickness 0 \
248            -image [Rappture::icon zoom-in] \
249            -command [itcl::code $this Zoom in]
250    } {
251        usual
252        ignore -highlightthickness
253    }
254    pack $itk_component(zoomin) -side top -padx 2 -pady 2
255    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
256
257    itk_component add zoomout {
258        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
259            -highlightthickness 0 \
260            -image [Rappture::icon zoom-out] \
261            -command [itcl::code $this Zoom out]
262    } {
263        usual
264        ignore -highlightthickness
265    }
266    pack $itk_component(zoomout) -side top -padx 2 -pady 2
267    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
268
269    itk_component add volume {
270        Rappture::PushButton $f.volume \
271            -onimage [Rappture::icon volume-on] \
272            -offimage [Rappture::icon volume-off] \
273            -command [itcl::code $this FixSettings volume] \
274            -variable [itcl::scope _settings($this-volume)]
275    }
276    $itk_component(volume) select
277    Rappture::Tooltip::for $itk_component(volume) \
278        "Toggle the volume cloud on/off"
279    pack $itk_component(volume) -padx 2 -pady 2
280
281    BuildViewTab
282    BuildVolumeTab
283    BuildCutplanesTab
284    BuildCameraTab
285
286    # Legend
287
288    set _image(legend) [image create photo]
289    itk_component add legend {
290        canvas $itk_component(plotarea).legend -height 50 -highlightthickness 0
291    } {
292        usual
293        ignore -highlightthickness
294        rename -background -plotbackground plotBackground Background
295    }
296    bind $itk_component(legend) <Configure> \
297        [itcl::code $this EventuallyResizeLegend]
298
299    # Hack around the Tk panewindow.  The problem is that the requested
300    # size of the 3d view isn't set until an image is retrieved from
301    # the server.  So the panewindow uses the tiny size.
302    set w 10000
303    pack forget $itk_component(3dview)
304    blt::table $itk_component(plotarea) \
305        0,0 $itk_component(3dview) -fill both -reqwidth $w \
306        1,0 $itk_component(legend) -fill x
307    blt::table configure $itk_component(plotarea) r1 -resize none
308
309    # Bindings for rotation via mouse
310    bind $itk_component(3dview) <ButtonPress-1> \
311        [itcl::code $this Rotate click %x %y]
312    bind $itk_component(3dview) <B1-Motion> \
313        [itcl::code $this Rotate drag %x %y]
314    bind $itk_component(3dview) <ButtonRelease-1> \
315        [itcl::code $this Rotate release %x %y]
316    bind $itk_component(3dview) <Configure> \
317        [itcl::code $this EventuallyResize %w %h]
318
319    # Bindings for panning via mouse
320    bind $itk_component(3dview) <ButtonPress-2> \
321        [itcl::code $this Pan click %x %y]
322    bind $itk_component(3dview) <B2-Motion> \
323        [itcl::code $this Pan drag %x %y]
324    bind $itk_component(3dview) <ButtonRelease-2> \
325        [itcl::code $this Pan release %x %y]
326
327    # Bindings for panning via keyboard
328    bind $itk_component(3dview) <KeyPress-Left> \
329        [itcl::code $this Pan set -10 0]
330    bind $itk_component(3dview) <KeyPress-Right> \
331        [itcl::code $this Pan set 10 0]
332    bind $itk_component(3dview) <KeyPress-Up> \
333        [itcl::code $this Pan set 0 -10]
334    bind $itk_component(3dview) <KeyPress-Down> \
335        [itcl::code $this Pan set 0 10]
336    bind $itk_component(3dview) <Shift-KeyPress-Left> \
337        [itcl::code $this Pan set -2 0]
338    bind $itk_component(3dview) <Shift-KeyPress-Right> \
339        [itcl::code $this Pan set 2 0]
340    bind $itk_component(3dview) <Shift-KeyPress-Up> \
341        [itcl::code $this Pan set 0 -2]
342    bind $itk_component(3dview) <Shift-KeyPress-Down> \
343        [itcl::code $this Pan set 0 2]
344
345    # Bindings for zoom via keyboard
346    bind $itk_component(3dview) <KeyPress-Prior> \
347        [itcl::code $this Zoom out]
348    bind $itk_component(3dview) <KeyPress-Next> \
349        [itcl::code $this Zoom in]
350
351    bind $itk_component(3dview) <Enter> "focus $itk_component(3dview)"
352
353    if {[string equal "x11" [tk windowingsystem]]} {
354        # Bindings for zoom via mouse
355        bind $itk_component(3dview) <4> [itcl::code $this Zoom out]
356        bind $itk_component(3dview) <5> [itcl::code $this Zoom in]
357    }
358
359    set _image(download) [image create photo]
360
361    eval itk_initialize $args
362
363    Connect
364}
365
366# ----------------------------------------------------------------------
367# DESTRUCTOR
368# ----------------------------------------------------------------------
369itcl::body Rappture::NanovisViewer::destructor {} {
370    $_dispatcher cancel !rebuild
371    $_dispatcher cancel !send_transfunc
372    $_dispatcher cancel !resize
373    image delete $_image(plot)
374    image delete $_image(legend)
375    image delete $_image(download)
376    array unset _settings $this-*
377}
378
379# ----------------------------------------------------------------------
380# USAGE: add <dataobj> ?<settings>?
381#
382# Clients use this to add a data object to the plot.  The optional
383# <settings> are used to configure the plot.  Allowed settings are
384# -color, -brightness, -width, -linestyle, and -raise.
385# ----------------------------------------------------------------------
386itcl::body Rappture::NanovisViewer::add {dataobj {settings ""}} {
387    array set params {
388        -color auto
389        -width 1
390        -linestyle solid
391        -brightness 0
392        -raise 0
393        -description ""
394        -param ""
395    }
396    foreach {opt val} $settings {
397        if {![info exists params($opt)]} {
398            error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]"
399        }
400        set params($opt) $val
401    }
402    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
403        # can't handle -autocolors yet
404        set params(-color) black
405    }
406    set pos [lsearch -exact $dataobj $_dlist]
407    if {$pos < 0} {
408        lappend _dlist $dataobj
409        set _allDataObjs($dataobj) 1
410        set _obj2ovride($dataobj-color) $params(-color)
411        set _obj2ovride($dataobj-width) $params(-width)
412        set _obj2ovride($dataobj-raise) $params(-raise)
413        $_dispatcher event -idle !rebuild
414    }
415}
416
417# ----------------------------------------------------------------------
418# USAGE: get ?-objects?
419# USAGE: get ?-image 3dview|legend?
420#
421# Clients use this to query the list of objects being plotted, in
422# order from bottom to top of this result.  The optional "-image"
423# flag can also request the internal images being shown.
424# ----------------------------------------------------------------------
425itcl::body Rappture::NanovisViewer::get {args} {
426    if {[llength $args] == 0} {
427        set args "-objects"
428    }
429
430    set op [lindex $args 0]
431    switch -- $op {
432      -objects {
433        # put the dataobj list in order according to -raise options
434        set dlist $_dlist
435        foreach obj $dlist {
436            if {[info exists _obj2ovride($obj-raise)] && $_obj2ovride($obj-raise)} {
437                set i [lsearch -exact $dlist $obj]
438                if {$i >= 0} {
439                    set dlist [lreplace $dlist $i $i]
440                    lappend dlist $obj
441                }
442            }
443        }
444        return $dlist
445      }
446      -image {
447        if {[llength $args] != 2} {
448            error "wrong # args: should be \"get -image 3dview|legend\""
449        }
450        switch -- [lindex $args end] {
451            3dview {
452                return $_image(plot)
453            }
454            legend {
455                return $_image(legend)
456            }
457            default {
458                error "bad image name \"[lindex $args end]\": should be 3dview or legend"
459            }
460        }
461      }
462      default {
463        error "bad option \"$op\": should be -objects or -image"
464      }
465    }
466}
467
468# ----------------------------------------------------------------------
469# USAGE: delete ?<dataobj1> <dataobj2> ...?
470#
471#       Clients use this to delete a dataobj from the plot.  If no dataobjs
472#       are specified, then all dataobjs are deleted.  No data objects are
473#       deleted.  They are only removed from the display list.
474#
475# ----------------------------------------------------------------------
476itcl::body Rappture::NanovisViewer::delete {args} {
477    if {[llength $args] == 0} {
478        set args $_dlist
479    }
480    # Delete all specified dataobjs
481    set changed 0
482    foreach dataobj $args {
483        set pos [lsearch -exact $_dlist $dataobj]
484        if { $pos >= 0 } {
485            set _dlist [lreplace $_dlist $pos $pos]
486            array unset _limits $dataobj*
487            array unset _obj2ovride $dataobj-*
488            array unset _vol2style $dataobj-*
489            set changed 1
490        }
491    }
492    # If anything changed, then rebuild the plot
493    if {$changed} {
494        $_dispatcher event -idle !rebuild
495    }
496}
497
498# ----------------------------------------------------------------------
499# USAGE: scale ?<data1> <data2> ...?
500#
501# Sets the default limits for the overall plot according to the
502# limits of the data for all of the given <data> objects.  This
503# accounts for all objects--even those not showing on the screen.
504# Because of this, the limits are appropriate for all objects as
505# the user scans through data in the ResultSet viewer.
506# ----------------------------------------------------------------------
507itcl::body Rappture::NanovisViewer::scale {args} {
508    foreach val {xmin xmax ymin ymax zmin zmax vmin vmax} {
509        set _limits($val) ""
510    }
511    foreach obj $args {
512        foreach axis {x y z v} {
513
514            foreach { min max } [$obj limits $axis] break
515
516            if {"" != $min && "" != $max} {
517                if {"" == $_limits(${axis}min)} {
518                    set _limits(${axis}min) $min
519                    set _limits(${axis}max) $max
520                } else {
521                    if {$min < $_limits(${axis}min)} {
522                        set _limits(${axis}min) $min
523                    }
524                    if {$max > $_limits(${axis}max)} {
525                        set _limits(${axis}max) $max
526                    }
527                }
528            }
529        }
530    }
531}
532
533# ----------------------------------------------------------------------
534# USAGE: download coming
535# USAGE: download controls <downloadCommand>
536# USAGE: download now
537#
538# Clients use this method to create a downloadable representation
539# of the plot.  Returns a list of the form {ext string}, where
540# "ext" is the file extension (indicating the type of data) and
541# "string" is the data itself.
542# ----------------------------------------------------------------------
543itcl::body Rappture::NanovisViewer::download {option args} {
544    switch $option {
545        coming {
546            if {[catch {
547                blt::winop snap $itk_component(plotarea) $_image(download)
548            }]} {
549                $_image(download) configure -width 1 -height 1
550                $_image(download) put #000000
551            }
552        }
553        controls {
554            # no controls for this download yet
555            return ""
556        }
557        now {
558            # Get the image data (as base64) and decode it back to binary.
559            # This is better than writing to temporary files.  When we switch
560            # to the BLT picture image it won't be necessary to decode the
561            # image data.
562            if { [image width $_image(plot)] > 0 &&
563                 [image height $_image(plot)] > 0 } {
564                set bytes [$_image(plot) data -format "jpeg -quality 100"]
565                set bytes [Rappture::encoding::decode -as b64 $bytes]
566                return [list .jpg $bytes]
567            }
568            return ""
569        }
570        default {
571            error "bad option \"$option\": should be coming, controls, now"
572        }
573    }
574}
575
576# ----------------------------------------------------------------------
577# USAGE: Connect ?<host:port>,<host:port>...?
578#
579# Clients use this method to establish a connection to a new
580# server, or to reestablish a connection to the previous server.
581# Any existing connection is automatically closed.
582# ----------------------------------------------------------------------
583itcl::body Rappture::NanovisViewer::Connect {} {
584    set _hosts [GetServerList "nanovis"]
585    if { "" == $_hosts } {
586        return 0
587    }
588    set result [VisViewer::Connect $_hosts]
589    if { $result } {
590        set w [winfo width $itk_component(3dview)]
591        set h [winfo height $itk_component(3dview)]
592        EventuallyResize $w $h
593    }
594    return $result
595}
596
597#
598# isconnected --
599#
600#       Indicates if we are currently connected to the visualization server.
601#
602itcl::body Rappture::NanovisViewer::isconnected {} {
603    return [VisViewer::IsConnected]
604}
605
606#
607# disconnect --
608#
609itcl::body Rappture::NanovisViewer::disconnect {} {
610    Disconnect
611}
612
613#
614# Disconnect --
615#
616#       Clients use this method to disconnect from the current rendering
617#       server.
618#
619itcl::body Rappture::NanovisViewer::Disconnect {} {
620    VisViewer::Disconnect
621
622    # disconnected -- no more data sitting on server
623    set _outbuf ""
624    array unset _serverVols
625}
626
627#
628# sendto --
629#
630itcl::body Rappture::NanovisViewer::sendto { bytes } {
631    SendBytes "$bytes\n"
632}
633
634#
635# SendCmd
636#
637#       Send commands off to the rendering server.  If we're currently
638#       sending data objects to the server, buffer the commands to be
639#       sent later.
640#
641itcl::body Rappture::NanovisViewer::SendCmd {string} {
642    if { $_buffering } {
643        append _outbuf $string "\n"
644    } else {
645        foreach line [split $string \n] {
646            SendEcho >>line $line
647        }
648        SendBytes "$string\n"
649    }
650}
651
652
653# ----------------------------------------------------------------------
654# USAGE: SendTransferFuncs
655# ----------------------------------------------------------------------
656itcl::body Rappture::NanovisViewer::SendTransferFuncs {} {
657    if { $_first == "" } {
658        puts stderr "first not set"
659        return
660    }
661    # Ensure that the global opacity and thickness settings (in the slider
662    # settings widgets) are used for the active transfer-function.  Update
663    # the values in the _settings varible.
664    set opacity [expr { double($_settings($this-opacity)) * 0.01 }]
665    # Scale values between 0.00001 and 0.01000
666    set thickness [expr {double($_settings($this-thickness)) * 0.0001}]
667
668    foreach vol [CurrentVolumes] {
669        if { ![info exists _serverVols($vol)] || !$_serverVols($vol) } {
670            # The volume hasn't reached the server yet.  How did we get
671            # here?
672            continue
673        }
674        if { ![info exists _vol2style($vol)] } {
675            puts stderr "unknown volume $vol"
676            continue;                        # How does this happen?
677        }
678        set tf $_vol2style($vol)
679        set _settings($this-$tf-opacity) $opacity
680        set _settings($this-$tf-thickness) $thickness
681        ComputeTransferFunc $tf
682        # FIXME: Need to the send information as to what transfer functions
683        #        to update so that we only update the transfer function
684        #        as necessary.  Right now, all transfer functions are
685        #        updated. This makes moving the isomarker slider chunky.
686        if { ![info exists _activeTfs($tf)] || !$_activeTfs($tf) } {
687            set _activeTfs($tf) 1
688        }
689        SendCmd "volume shading transfunc $tf $vol"
690    }
691    FixLegend
692}
693
694# ----------------------------------------------------------------------
695# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
696#
697# Invoked automatically whenever the "image" command comes in from
698# the rendering server.  Indicates that binary image data with the
699# specified <size> will follow.
700# ----------------------------------------------------------------------
701itcl::body Rappture::NanovisViewer::ReceiveImage { args } {
702    array set info {
703        -token "???"
704        -bytes 0
705        -type image
706    }
707    array set info $args
708    set bytes [ReceiveBytes $info(-bytes)]
709    ReceiveEcho <<line "<read $info(-bytes) bytes"
710    if { $info(-type) == "image" } {
711        ReceiveEcho "for [image width $_image(plot)]x[image height $_image(plot)] image>"       
712        $_image(plot) configure -data $bytes
713    } elseif { $info(type) == "print" } {
714        set tag $this-print-$info(-token)
715        set _hardcopy($tag) $bytes
716    }
717}
718
719#
720# ReceiveLegend --
721#
722#       The procedure is the response from the render server to each "legend"
723#       command.  The server sends back a "legend" command invoked our
724#       the slave interpreter.  The purpose is to collect data of the image
725#       representing the legend in the canvas.  In addition, the isomarkers
726#       of the active transfer function are displayed.
727#
728#       I don't know is this is the right place to display the isomarkers.
729#       I don't know all the different paths used to draw the plot. There's
730#       "Rebuild", "add", etc.
731#
732itcl::body Rappture::NanovisViewer::ReceiveLegend { tf vmin vmax size } {
733    if { ![isconnected] } {
734        return
735    }
736    set bytes [ReceiveBytes $size]
737    $_image(legend) configure -data $bytes
738    ReceiveEcho <<line "<read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
739
740    set c $itk_component(legend)
741    set w [winfo width $c]
742    set h [winfo height $c]
743    set lx 10
744    set ly [expr {$h - 1}]
745    if {"" == [$c find withtag transfunc]} {
746        $c create image 10 10 -anchor nw \
747            -image $_image(legend) -tags transfunc
748        $c create text $lx $ly -anchor sw \
749            -fill $itk_option(-plotforeground) -tags "limits vmin"
750        $c create text [expr {$w-$lx}] $ly -anchor se \
751            -fill $itk_option(-plotforeground) -tags "limits vmax"
752        $c lower transfunc
753        $c bind transfunc <ButtonRelease-1> \
754            [itcl::code $this AddIsoMarker %x %y]
755    }
756    # Display the markers used by the active transfer function.
757
758    array set limits [limits $tf]
759    $c itemconfigure vmin -text [format %.2g $limits(min)]
760    $c coords vmin $lx $ly
761
762    $c itemconfigure vmax -text [format %.2g $limits(max)]
763    $c coords vmax [expr {$w-$lx}] $ly
764
765    if { [info exists _isomarkers($tf)] } {
766        foreach m $_isomarkers($tf) {
767            $m visible yes
768        }
769    }
770
771    # The colormap may have changed. Resync the slicers with the colormap.
772    set vols [CurrentVolumes -cutplanes]
773    SendCmd "volume data state $_settings($this-volume) $vols"
774
775    # Adjust the cutplane for only the first component in the topmost volume
776    # (i.e. the first volume designated in the field).
777    set vol [lindex $vols 0]
778    foreach axis {x y z} {
779        # Turn off cutplanes for all volumes
780        SendCmd "cutplane state 0 $axis"
781        if { $_settings($this-${axis}cutplane) } {
782            # Turn on cutplane for this particular volume and set the position
783            SendCmd "cutplane state 1 $axis $vol"
784            set pos [expr {0.01*$_settings($this-${axis}cutposition)}]
785            SendCmd "cutplane position $pos $axis $vol"
786        }
787    }
788}
789
790#
791# ReceiveData --
792#
793#       The procedure is the response from the render server to each "data
794#       follows" command.  The server sends back a "data" command invoked our
795#       the slave interpreter.  The purpose is to collect the min/max of the
796#       volume sent to the render server.  Since the client (nanovisviewer)
797#       doesn't parse 3D data formats, we rely on the server (nanovis) to
798#       tell us what the limits are.  Once we've received the limits to all
799#       the data we've sent (tracked by _recvdVols) we can then determine
800#       what the transfer functions are for these volumes.
801#
802#
803#       Note: There is a considerable tradeoff in having the server report
804#             back what the data limits are.  It means that much of the code
805#             having to do with transfer-functions has to wait for the data
806#             to come back, since the isomarkers are calculated based upon
807#             the data limits.  The client code is much messier because of
808#             this.  The alternative is to parse any of the 3D formats on the
809#             client side.
810#
811itcl::body Rappture::NanovisViewer::ReceiveData { args } {
812    if { ![isconnected] } {
813        return
814    }
815    # Arguments from server are name value pairs. Stuff them in an array.
816    array set info $args
817
818    set tag $info(tag)
819    set parts [split $tag -]
820
821    #
822    # Volumes don't exist until we're told about them.
823    #
824    set dataobj [lindex $parts 0]
825    set _serverVols($tag) 1
826    if { $_settings($this-volume) && $dataobj == $_first } {
827        SendCmd "volume state 1 $tag"
828    }
829    set _limits($tag-min) $info(min);  # Minimum value of the volume.
830    set _limits($tag-max) $info(max);  # Maximum value of the volume.
831    set _limits(vmin)      $info(vmin); # Overall minimum value.
832    set _limits(vmax)      $info(vmax); # Overall maximum value.
833
834    unset _recvdVols($tag)
835    if { [array size _recvdVols] == 0 } {
836        # The active transfer function is by default the first component of
837        # the first data object.  This assumes that the data is always
838        # successfully transferred.
839        updatetransferfuncs
840    }
841}
842
843# ----------------------------------------------------------------------
844# USAGE: Rebuild
845#
846# Called automatically whenever something changes that affects the
847# data in the widget.  Clears any existing data and rebuilds the
848# widget to display new data.
849# ----------------------------------------------------------------------
850itcl::body Rappture::NanovisViewer::Rebuild {} {
851
852    # Turn on buffering of commands to the server.  We don't want to
853    # be preempted by a server disconnect/reconnect (which automatically
854    # generates a new call to Rebuild).   
855    set _buffering 1
856
857    # Hide all the isomarkers. Can't remove them. Have to remember the
858    # settings since the user may have created/deleted/moved markers.
859
860    foreach tf [array names _isomarkers] {
861        foreach m $_isomarkers($tf) {
862            $m visible no
863        }
864    }
865
866    set w [winfo width $itk_component(3dview)]
867    set h [winfo height $itk_component(3dview)]
868    EventuallyResize $w $h
869
870    foreach dataobj [get] {
871        foreach comp [$dataobj components] {
872            set vol $dataobj-$comp
873            if { ![info exists _serverVols($vol)] } {
874                # Send the data as one huge base64-encoded mess -- yuck!
875                set data [$dataobj values $comp]
876                set nbytes [string length $data]
877                append _outbuf "volume data follows $nbytes $vol\n"
878                append _outbuf $data
879                set _recvdVols($vol) 1
880                set _serverVols($vol) 0
881            }
882            NameTransferFunc $dataobj $comp
883        }
884    }
885    #
886    # Reset the camera and other view parameters
887    #
888
889    set _settings($this-theta) $_view(theta)
890    set _settings($this-phi)   $_view(phi)
891    set _settings($this-psi)   $_view(psi)
892    set _settings($this-pan-x) $_view(pan-x)
893    set _settings($this-pan-y) $_view(pan-y)
894    set _settings($this-zoom)  $_view(zoom)
895
896    set xyz [Euler2XYZ $_view(theta) $_view(phi) $_view(psi)]
897    SendCmd "camera angle $xyz"
898    PanCamera
899    SendCmd "camera zoom $_view(zoom)"
900    FixSettings light
901    FixSettings transp
902    FixSettings isosurface
903    FixSettings grid
904    FixSettings axes
905    FixSettings outline
906
907    # nothing to send -- activate the proper ivol
908    SendCmd "volume state 0"
909    set _first [lindex [get] 0]
910    if {"" != $_first} {
911        set axis [$_first hints updir]
912        if { "" != $axis } {
913            SendCmd "up $axis"
914        }
915        set location [$_first hints camera]
916        if { $location != "" } {
917            array set _view $location
918        }
919        set vols [array names _serverVols $_first-*]
920        if { $vols != "" } {
921            SendCmd "volume state 1 $vols"
922        }
923    }
924    # If the first volume already exists on the server, then make sure we
925    # display the proper transfer function in the legend.
926    set comp [lindex [$_first components] 0]
927    if { [info exists _serverVols($_first-$comp)] } {
928        updatetransferfuncs
929    }
930    foreach axis {x y z} {
931        # Turn off cutplanes for all volumes
932        SendCmd "cutplane state 0 $axis"
933    }
934    set _buffering 0;                        # Turn off buffering.
935    # Actually write the commands to the server socket.  If it fails, we don't
936    # care.  We're finished here.
937    blt::busy hold $itk_component(hull)
938    SendBytes $_outbuf;                       
939    blt::busy release $itk_component(hull)
940    set _outbuf "";                        # Clear the buffer.               
941}
942
943# ----------------------------------------------------------------------
944# USAGE: CurrentVolumes ?-cutplanes?
945#
946# Returns a list of volume server IDs for the current volume being
947# displayed.  This is normally a single ID, but it might be a list
948# of IDs if the current data object has multiple components.
949# ----------------------------------------------------------------------
950itcl::body Rappture::NanovisViewer::CurrentVolumes {{what -all}} {
951    set rlist ""
952    if { $_first == "" } {
953        return
954    }
955    foreach comp [$_first components] {
956        set vol $_first-$comp
957        if { [info exists _serverVols($vol)] && $_serverVols($vol) } {
958            array set style {
959                -cutplanes 1
960            }
961            array set style [lindex [$_first components -style $comp] 0]
962            if { $what != "-cutplanes" || $style(-cutplanes) } {
963                lappend rlist $vol
964            }
965        }
966    }
967    return $rlist
968}
969
970# ----------------------------------------------------------------------
971# USAGE: Zoom in
972# USAGE: Zoom out
973# USAGE: Zoom reset
974#
975# Called automatically when the user clicks on one of the zoom
976# controls for this widget.  Changes the zoom for the current view.
977# ----------------------------------------------------------------------
978itcl::body Rappture::NanovisViewer::Zoom {option} {
979    switch -- $option {
980        "in" {
981            set _view(zoom) [expr {$_view(zoom)*1.25}]
982            set _settings($this-zoom) $_view(zoom)
983        }
984        "out" {
985            set _view(zoom) [expr {$_view(zoom)*0.8}]
986            set _settings($this-zoom) $_view(zoom)
987        }
988        "reset" {
989            array set _view {
990                theta   45
991                phi     45
992                psi     0
993                zoom    1.0
994                pan-x   0
995                pan-y   0
996            }
997            if { $_first != "" } {
998                set location [$_first hints camera]
999                if { $location != "" } {
1000                    array set _view $location
1001                }
1002            }
1003            set xyz [Euler2XYZ $_view(theta) $_view(phi) $_view(psi)]
1004            SendCmd "camera angle $xyz"
1005            PanCamera
1006            set _settings($this-theta) $_view(theta)
1007            set _settings($this-phi)   $_view(phi)
1008            set _settings($this-psi)   $_view(psi)
1009            set _settings($this-pan-x) $_view(pan-x)
1010            set _settings($this-pan-y) $_view(pan-y)
1011            set _settings($this-zoom)  $_view(zoom)
1012        }
1013    }
1014    SendCmd "camera zoom $_view(zoom)"
1015}
1016
1017itcl::body Rappture::NanovisViewer::PanCamera {} {
1018    #set x [expr ($_view(pan-x)) / $_limits(xrange)]
1019    #set y [expr ($_view(pan-y)) / $_limits(yrange)]
1020    set x $_view(pan-x)
1021    set y $_view(pan-y)
1022    SendCmd "camera pan $x $y"
1023}
1024
1025
1026# ----------------------------------------------------------------------
1027# USAGE: Rotate click <x> <y>
1028# USAGE: Rotate drag <x> <y>
1029# USAGE: Rotate release <x> <y>
1030#
1031# Called automatically when the user clicks/drags/releases in the
1032# plot area.  Moves the plot according to the user's actions.
1033# ----------------------------------------------------------------------
1034itcl::body Rappture::NanovisViewer::Rotate {option x y} {
1035    switch -- $option {
1036        click {
1037            $itk_component(3dview) configure -cursor fleur
1038            set _click(x) $x
1039            set _click(y) $y
1040            set _click(theta) $_view(theta)
1041            set _click(phi) $_view(phi)
1042        }
1043        drag {
1044            if {[array size _click] == 0} {
1045                Rotate click $x $y
1046            } else {
1047                set w [winfo width $itk_component(3dview)]
1048                set h [winfo height $itk_component(3dview)]
1049                if {$w <= 0 || $h <= 0} {
1050                    return
1051                }
1052
1053                if {[catch {
1054                    # this fails sometimes for no apparent reason
1055                    set dx [expr {double($x-$_click(x))/$w}]
1056                    set dy [expr {double($y-$_click(y))/$h}]
1057                }]} {
1058                    return
1059                }
1060
1061                #
1062                # Rotate the camera in 3D
1063                #
1064                if {$_view(psi) > 90 || $_view(psi) < -90} {
1065                    # when psi is flipped around, theta moves backwards
1066                    set dy [expr {-$dy}]
1067                }
1068                set theta [expr {$_view(theta) - $dy*180}]
1069                while {$theta < 0} { set theta [expr {$theta+180}] }
1070                while {$theta > 180} { set theta [expr {$theta-180}] }
1071
1072                if {abs($theta) >= 30 && abs($theta) <= 160} {
1073                    set phi [expr {$_view(phi) - $dx*360}]
1074                    while {$phi < 0} { set phi [expr {$phi+360}] }
1075                    while {$phi > 360} { set phi [expr {$phi-360}] }
1076                    set psi $_view(psi)
1077                } else {
1078                    set phi $_view(phi)
1079                    set psi [expr {$_view(psi) - $dx*360}]
1080                    while {$psi < -180} { set psi [expr {$psi+360}] }
1081                    while {$psi > 180} { set psi [expr {$psi-360}] }
1082                }
1083
1084                set _view(theta)        $theta
1085                set _view(phi)          $phi
1086                set _view(psi)          $psi
1087                set xyz [Euler2XYZ $theta $phi $psi]
1088                set _settings($this-theta) $_view(theta)
1089                set _settings($this-phi)   $_view(phi)
1090                set _settings($this-psi)   $_view(psi)
1091                SendCmd "camera angle $xyz"
1092                set _click(x) $x
1093                set _click(y) $y
1094            }
1095        }
1096        release {
1097            Rotate drag $x $y
1098            $itk_component(3dview) configure -cursor ""
1099            catch {unset _click}
1100        }
1101        default {
1102            error "bad option \"$option\": should be click, drag, release"
1103        }
1104    }
1105}
1106
1107# ----------------------------------------------------------------------
1108# USAGE: $this Pan click x y
1109#        $this Pan drag x y
1110#        $this Pan release x y
1111#
1112# Called automatically when the user clicks on one of the zoom
1113# controls for this widget.  Changes the zoom for the current view.
1114# ----------------------------------------------------------------------
1115itcl::body Rappture::NanovisViewer::Pan {option x y} {
1116    # Experimental stuff
1117    set w [winfo width $itk_component(3dview)]
1118    set h [winfo height $itk_component(3dview)]
1119    if { $option == "set" } {
1120        set x [expr $x / double($w)]
1121        set y [expr $y / double($h)]
1122        set _view(pan-x) [expr $_view(pan-x) + $x]
1123        set _view(pan-y) [expr $_view(pan-y) + $y]
1124        PanCamera
1125        set _settings($this-pan-x) $_view(pan-x)
1126        set _settings($this-pan-y) $_view(pan-y)
1127        return
1128    }
1129    if { $option == "click" } {
1130        set _click(x) $x
1131        set _click(y) $y
1132        $itk_component(3dview) configure -cursor hand1
1133    }
1134    if { $option == "drag" || $option == "release" } {
1135        set dx [expr ($_click(x) - $x)/double($w)]
1136        set dy [expr ($_click(y) - $y)/double($h)]
1137        set _click(x) $x
1138        set _click(y) $y
1139        set _view(pan-x) [expr $_view(pan-x) - $dx]
1140        set _view(pan-y) [expr $_view(pan-y) - $dy]
1141        PanCamera
1142        set _settings($this-pan-x) $_view(pan-x)
1143        set _settings($this-pan-y) $_view(pan-y)
1144    }
1145    if { $option == "release" } {
1146        $itk_component(3dview) configure -cursor ""
1147    }
1148}
1149
1150# ----------------------------------------------------------------------
1151# USAGE: FixSettings <what> ?<value>?
1152#
1153# Used internally to update rendering settings whenever parameters
1154# change in the popup settings panel.  Sends the new settings off
1155# to the back end.
1156# ----------------------------------------------------------------------
1157itcl::body Rappture::NanovisViewer::FixSettings {what {value ""}} {
1158    switch -- $what {
1159        light {
1160            if {[isconnected]} {
1161                set val $_settings($this-light)
1162                set sval [expr {0.01*$val}]
1163                SendCmd "volume shading diffuse $sval"
1164                set sval [expr {sqrt($val+1.0)}]
1165                SendCmd "volume shading specular $sval"
1166            }
1167        }
1168        transp {
1169            if {[isconnected]} {
1170                set val $_settings($this-transp)
1171                set sval [expr {0.2*$val+1}]
1172                SendCmd "volume shading opacity $sval"
1173            }
1174        }
1175        opacity {
1176            if {[isconnected] && [array size _activeTfs] > 0 } {
1177                set val $_settings($this-opacity)
1178                set sval [expr { 0.01 * double($val) }]
1179                foreach tf [array names _activeTfs] {
1180                    set _settings($this-$tf-opacity) $sval
1181                    set _activeTfs($tf) 0
1182                }
1183                updatetransferfuncs
1184            }
1185        }
1186
1187        thickness {
1188            if {[isconnected] && [array names _activeTfs] > 0 } {
1189                set val $_settings($this-thickness)
1190                # Scale values between 0.00001 and 0.01000
1191                set sval [expr {0.0001*double($val)}]
1192                foreach tf [array names _activeTfs] {
1193                    set _settings($this-$tf-thickness) $sval
1194                    set _activeTfs($tf) 0
1195                }
1196                updatetransferfuncs
1197            }
1198        }
1199        "outline" {
1200            if {[isconnected]} {
1201                SendCmd "volume outline state $_settings($this-outline)"
1202            }
1203        }
1204        "isosurface" {
1205            if {[isconnected]} {
1206                SendCmd "volume shading isosurface $_settings($this-isosurface)"
1207            }
1208        }
1209        "grid" {
1210            if { [isconnected] } {
1211                SendCmd "grid visible $_settings($this-grid)"
1212            }
1213        }
1214        "axes" {
1215            if { [isconnected] } {
1216                SendCmd "axis visible $_settings($this-axes)"
1217            }
1218        }
1219        "legend" {
1220            if { $_settings($this-legend) } {
1221                blt::table $itk_component(plotarea) \
1222                    0,0 $itk_component(3dview) -fill both \
1223                    1,0 $itk_component(legend) -fill x
1224                blt::table configure $itk_component(plotarea) r1 -resize none
1225            } else {
1226                blt::table forget $itk_component(legend)
1227            }
1228        }
1229        "volume" {
1230            if { [isconnected] } {
1231                set vols [CurrentVolumes -cutplanes]
1232                SendCmd "volume data state $_settings($this-volume) $vols"
1233            }
1234        }
1235        "xcutplane" - "ycutplane" - "zcutplane" {
1236            set axis [string range $what 0 0]
1237            set bool $_settings($this-$what)
1238            if { [isconnected] } {
1239                set vols [CurrentVolumes -cutplanes]
1240                set vol [lindex $vols 0]
1241                SendCmd "cutplane state $bool $axis $vol"
1242            }
1243            if { $bool } {
1244                $itk_component(${axis}CutScale) configure -state normal \
1245                    -troughcolor white
1246            } else {
1247                $itk_component(${axis}CutScale) configure -state disabled \
1248                    -troughcolor grey82
1249            }
1250        }
1251        default {
1252            error "don't know how to fix $what"
1253        }
1254    }
1255}
1256
1257# ----------------------------------------------------------------------
1258# USAGE: FixLegend
1259#
1260# Used internally to update the legend area whenever it changes size
1261# or when the field changes.  Asks the server to send a new legend
1262# for the current field.
1263# ----------------------------------------------------------------------
1264itcl::body Rappture::NanovisViewer::FixLegend {} {
1265    set _resizeLegendPending 0
1266    set lineht [font metrics $itk_option(-font) -linespace]
1267    set w [expr {$_width-20}]
1268    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
1269    if {$w > 0 && $h > 0 && [array names _activeTfs] > 0 && $_first != "" } {
1270        set vol [lindex [CurrentVolumes] 0]
1271        if { [info exists _vol2style($vol)] } {
1272            SendCmd "legend $_vol2style($vol) $w $h"
1273        }
1274    } else {
1275        # Can't do this as this will remove the items associated with the
1276        # isomarkers.
1277       
1278        #$itk_component(legend) delete all
1279    }
1280}
1281
1282#
1283# NameTransferFunc --
1284#
1285#       Creates a transfer function name based on the <style> settings in the
1286#       library run.xml file. This placeholder will be used later to create
1287#       and send the actual transfer function once the data info has been sent
1288#       to us by the render server. [We won't know the volume limits until the
1289#       server parses the 3D data and sends back the limits via ReceiveData.]
1290#
1291#       FIXME: The current way we generate transfer-function names completely
1292#              ignores the -markers option.  The problem is that we are forced
1293#              to compute the name from an increasing complex set of values:
1294#              color, levels, marker, opacity.  I think we're stuck doing it
1295#              now.
1296#
1297itcl::body Rappture::NanovisViewer::NameTransferFunc { dataobj comp } {
1298    array set style {
1299        -color rainbow
1300        -levels 6
1301        -opacity 1.0
1302    }
1303    array set style [lindex [$dataobj components -style $comp] 0]
1304    set tf "$style(-color):$style(-levels):$style(-opacity)"
1305    set _vol2style($dataobj-$comp) $tf
1306    lappend _style2vols($tf) $dataobj-$comp
1307    return $tf
1308}
1309
1310#
1311# ComputeTransferFunc --
1312#
1313#   Computes and sends the transfer function to the render server.  It's
1314#   assumed that the volume data limits are known and that the global
1315#   transfer-functions slider values have be setup.  Both parts are
1316#   needed to compute the relative value (location) of the marker, and
1317#   the alpha map of the transfer function.
1318#
1319itcl::body Rappture::NanovisViewer::ComputeTransferFunc { tf } {
1320    array set style {
1321        -color rainbow
1322        -levels 6
1323        -opacity 1.0
1324    }
1325    foreach {dataobj comp} [split [lindex $_style2vols($tf) 0] -] break
1326    array set style [lindex [$dataobj components -style $comp] 0]
1327
1328
1329    # We have to parse the style attributes for a volume using this
1330    # transfer-function *once*.  This sets up the initial isomarkers for the
1331    # transfer function.  The user may add/delete markers, so we have to
1332    # maintain a list of markers for each transfer-function.  We use the one
1333    # of the volumes (the first in the list) using the transfer-function as a
1334    # reference.
1335    #
1336    # FIXME: The current way we generate transfer-function names completely
1337    #        ignores the -markers option.  The problem is that we are forced
1338    #        to compute the name from an increasing complex set of values:
1339    #        color, levels, marker, opacity.  I think the cow's out of the
1340    #        barn on this one.
1341
1342    if { ![info exists _isomarkers($tf)] } {
1343        # Have to defer creation of isomarkers until we have data limits
1344        if { [info exists style(-markers)] } {
1345            ParseMarkersOption $tf $style(-markers)
1346        } else {
1347            ParseLevelsOption $tf $style(-levels)
1348        }
1349    }
1350    if {$style(-color) == "rainbow"} {
1351        set style(-color) "white:yellow:green:cyan:blue:magenta"
1352    }
1353    set clist [split $style(-color) :]
1354    set cmap "0.0 [Color2RGB white] "
1355    for {set i 0} {$i < [llength $clist]} {incr i} {
1356        set x [expr {double($i+1)/([llength $clist]+1)}]
1357        set color [lindex $clist $i]
1358        append cmap "$x [Color2RGB $color] "
1359    }
1360    append cmap "1.0 [Color2RGB $color]"
1361
1362    set tag $this-$tf
1363    if { ![info exists _settings($tag-opacity)] } {
1364        set _settings($tag-opacity) $style(-opacity)
1365    }
1366    set max $_settings($tag-opacity)
1367
1368    set isovalues {}
1369    foreach m $_isomarkers($tf) {
1370        lappend isovalues [$m relval]
1371    }
1372    # Sort the isovalues
1373    set isovalues [lsort -real $isovalues]
1374
1375    if { ![info exists _settings($tag-thickness)]} {
1376        set _settings($tag-thickness) 0.05
1377    }
1378    set delta $_settings($tag-thickness)
1379
1380    set first [lindex $isovalues 0]
1381    set last [lindex $isovalues end]
1382    set wmap ""
1383    if { $first == "" || $first != 0.0 } {
1384        lappend wmap 0.0 0.0
1385    }
1386    foreach x $isovalues {
1387        set x1 [expr {$x-$delta-0.00001}]
1388        set x2 [expr {$x-$delta}]
1389        set x3 [expr {$x+$delta}]
1390        set x4 [expr {$x+$delta+0.00001}]
1391        if { $x1 < 0.0 } {
1392            set x1 0.0
1393        } elseif { $x1 > 1.0 } {
1394            set x1 1.0
1395        }
1396        if { $x2 < 0.0 } {
1397            set x2 0.0
1398        } elseif { $x2 > 1.0 } {
1399            set x2 1.0
1400        }
1401        if { $x3 < 0.0 } {
1402            set x3 0.0
1403        } elseif { $x3 > 1.0 } {
1404            set x3 1.0
1405        }
1406        if { $x4 < 0.0 } {
1407            set x4 0.0
1408        } elseif { $x4 > 1.0 } {
1409            set x4 1.0
1410        }
1411        # add spikes in the middle
1412        lappend wmap $x1 0.0
1413        lappend wmap $x2 $max
1414        lappend wmap $x3 $max
1415        lappend wmap $x4 0.0
1416    }
1417    if { $last == "" || $last != 1.0 } {
1418        lappend wmap 1.0 0.0
1419    }
1420    SendCmd "transfunc define $tf { $cmap } { $wmap }"
1421}
1422
1423# ----------------------------------------------------------------------
1424# CONFIGURATION OPTION: -plotbackground
1425# ----------------------------------------------------------------------
1426itcl::configbody Rappture::NanovisViewer::plotbackground {
1427    if { [isconnected] } {
1428        foreach {r g b} [Color2RGB $itk_option(-plotbackground)] break
1429        #fix this!
1430        #SendCmd "color background $r $g $b"
1431    }
1432}
1433
1434# ----------------------------------------------------------------------
1435# CONFIGURATION OPTION: -plotforeground
1436# ----------------------------------------------------------------------
1437itcl::configbody Rappture::NanovisViewer::plotforeground {
1438    if { [isconnected] } {
1439        foreach {r g b} [Color2RGB $itk_option(-plotforeground)] break
1440        #fix this!
1441        #SendCmd "color background $r $g $b"
1442    }
1443}
1444
1445# ----------------------------------------------------------------------
1446# CONFIGURATION OPTION: -plotoutline
1447# ----------------------------------------------------------------------
1448itcl::configbody Rappture::NanovisViewer::plotoutline {
1449    # Must check if we are connected because this routine is called from the
1450    # class body when the -plotoutline itk_option is defined.  At that point
1451    # the NanovisViewer class constructor hasn't been called, so we can't
1452    # start sending commands to visualization server.
1453    if { [isconnected] } {
1454        if {"" == $itk_option(-plotoutline)} {
1455            SendCmd "volume outline state off"
1456        } else {
1457            SendCmd "volume outline state on"
1458            SendCmd "volume outline color [Color2RGB $itk_option(-plotoutline)]"
1459        }
1460    }
1461}
1462
1463#
1464# The -levels option takes a single value that represents the number
1465# of evenly distributed markers based on the current data range. Each
1466# marker is a relative value from 0.0 to 1.0.
1467#
1468itcl::body Rappture::NanovisViewer::ParseLevelsOption { tf levels } {
1469    set c $itk_component(legend)
1470    regsub -all "," $levels " " levels
1471    if {[string is int $levels]} {
1472        for {set i 1} { $i <= $levels } {incr i} {
1473            set x [expr {double($i)/($levels+1)}]
1474            set m [Rappture::IsoMarker \#auto $c $this $tf]
1475            $m relval $x
1476            lappend _isomarkers($tf) $m
1477        }
1478    } else {
1479        foreach x $levels {
1480            set m [Rappture::IsoMarker \#auto $c $this $tf]
1481            $m relval $x
1482            lappend _isomarkers($tf) $m
1483        }
1484    }
1485}
1486
1487#
1488# The -markers option takes a list of zero or more values (the values
1489# may be separated either by spaces or commas) that have the following
1490# format:
1491#
1492#   N%  Percent of current total data range.  Converted to
1493#       to a relative value between 0.0 and 1.0.
1494#   N   Absolute value of marker.  If the marker is outside of
1495#       the current range, it will be displayed on the outer
1496#       edge of the legends, but it range it represents will
1497#       not be seen.
1498#
1499itcl::body Rappture::NanovisViewer::ParseMarkersOption { tf markers } {
1500    set c $itk_component(legend)
1501    regsub -all "," $markers " " markers
1502    foreach marker $markers {
1503        set n [scan $marker "%g%s" value suffix]
1504        if { $n == 2 && $suffix == "%" } {
1505            # ${n}% : Set relative value.
1506            set value [expr {$value * 0.01}]
1507            set m [Rappture::IsoMarker \#auto $c $this $tf]
1508            $m relval $value
1509            lappend _isomarkers($tf) $m
1510        } else {
1511            # ${n} : Set absolute value.
1512            set m [Rappture::IsoMarker \#auto $c $this $tf]
1513            $m absval $value
1514            lappend _isomarkers($tf) $m
1515        }
1516    }
1517}
1518
1519# ----------------------------------------------------------------------
1520# USAGE: UndateTransferFuncs
1521# ----------------------------------------------------------------------
1522itcl::body Rappture::NanovisViewer::updatetransferfuncs {} {
1523    $_dispatcher event -idle !send_transfunc
1524}
1525
1526itcl::body Rappture::NanovisViewer::AddIsoMarker { x y } {
1527    if { $_first == "" } {
1528        error "active transfer function isn't set"
1529    }
1530    set vol [lindex [CurrentVolumes] 0]
1531    set tf $_vol2style($vol)
1532    set c $itk_component(legend)
1533    set m [Rappture::IsoMarker \#auto $c $this $tf]
1534    set w [winfo width $c]
1535    $m relval [expr {double($x-10)/($w-20)}]
1536    lappend _isomarkers($tf) $m
1537    updatetransferfuncs
1538    return 1
1539}
1540
1541itcl::body Rappture::NanovisViewer::rmdupmarker { marker x } {
1542    set tf [$marker transferfunc]
1543    set bool 0
1544    if { [info exists _isomarkers($tf)] } {
1545        set list {}
1546        set marker [namespace tail $marker]
1547        foreach m $_isomarkers($tf) {
1548            set sx [$m screenpos]
1549            if { $m != $marker } {
1550                if { $x >= ($sx-3) && $x <= ($sx+3) } {
1551                    $marker relval [$m relval]
1552                    itcl::delete object $m
1553                    bell
1554                    set bool 1
1555                    continue
1556                }
1557            }
1558            lappend list $m
1559        }
1560        set _isomarkers($tf) $list
1561        updatetransferfuncs
1562    }
1563    return $bool
1564}
1565
1566itcl::body Rappture::NanovisViewer::overmarker { marker x } {
1567    set tf [$marker transferfunc]
1568    if { [info exists _isomarkers($tf)] } {
1569        set marker [namespace tail $marker]
1570        foreach m $_isomarkers($tf) {
1571            set sx [$m screenpos]
1572            if { $m != $marker } {
1573                set bool [expr { $x >= ($sx-3) && $x <= ($sx+3) }]
1574                $m activate $bool
1575            }
1576        }
1577    }
1578    return ""
1579}
1580
1581itcl::body Rappture::NanovisViewer::limits { tf } {
1582    set _limits(min) 0.0
1583    set _limits(max) 1.0
1584    if { ![info exists _style2vols($tf)] } {
1585        return [array get _limits]
1586    }
1587    set min ""; set max ""
1588    foreach vol $_style2vols($tf) {
1589        if { ![info exists _serverVols($vol)] } {
1590            continue
1591        }
1592        if { ![info exists _limits($vol-min)] } {
1593            continue
1594        }
1595        if { $min == "" || $min > $_limits($vol-min) } {
1596            set min $_limits($vol-min)
1597        }
1598        if { $max == "" || $max < $_limits($vol-max) } {
1599            set max $_limits($vol-max)
1600        }
1601    }
1602    if { $min != "" } {
1603        set _limits(min) $min
1604    }
1605    if { $max != "" } {
1606        set _limits(max) $max
1607    }
1608    return [array get _limits]
1609}
1610
1611
1612itcl::body Rappture::NanovisViewer::BuildViewTab {} {
1613    foreach { key value } {
1614        grid            0
1615        axes            1
1616        outline         1
1617        volume          1
1618        legend          1
1619        particles       1
1620        lic             1
1621    } {
1622        set _settings($this-$key) $value
1623    }
1624
1625    set fg [option get $itk_component(hull) font Font]
1626    #set bfg [option get $itk_component(hull) boldFont Font]
1627
1628    set inner [$itk_component(main) insert end \
1629        -title "View Settings" \
1630        -icon [Rappture::icon wrench]]
1631    $inner configure -borderwidth 4
1632
1633    set ::Rappture::NanovisViewer::_settings($this-isosurface) 0
1634    checkbutton $inner.isosurface \
1635        -text "Isosurface shading" \
1636        -variable [itcl::scope _settings($this-isosurface)] \
1637        -command [itcl::code $this FixSettings isosurface] \
1638        -font "Arial 9"
1639
1640    checkbutton $inner.axes \
1641        -text "Axes" \
1642        -variable [itcl::scope _settings($this-axes)] \
1643        -command [itcl::code $this FixSettings axes] \
1644        -font "Arial 9"
1645
1646    checkbutton $inner.grid \
1647        -text "Grid" \
1648        -variable [itcl::scope _settings($this-grid)] \
1649        -command [itcl::code $this FixSettings grid] \
1650        -font "Arial 9"
1651
1652    checkbutton $inner.outline \
1653        -text "Outline" \
1654        -variable [itcl::scope _settings($this-outline)] \
1655        -command [itcl::code $this FixSettings outline] \
1656        -font "Arial 9"
1657
1658    checkbutton $inner.legend \
1659        -text "Legend" \
1660        -variable [itcl::scope _settings($this-legend)] \
1661        -command [itcl::code $this FixSettings legend] \
1662        -font "Arial 9"
1663
1664    checkbutton $inner.volume \
1665        -text "Volume" \
1666        -variable [itcl::scope _settings($this-volume)] \
1667        -command [itcl::code $this FixSettings volume] \
1668        -font "Arial 9"
1669
1670    blt::table $inner \
1671        0,0 $inner.axes  -columnspan 2 -anchor w \
1672        1,0 $inner.grid  -columnspan 2 -anchor w \
1673        2,0 $inner.outline  -columnspan 2 -anchor w \
1674        3,0 $inner.volume  -columnspan 2 -anchor w \
1675        4,0 $inner.legend  -columnspan 2 -anchor w
1676
1677    if 0 {
1678    bind $inner <Map> [itcl::code $this GetVolumeInfo $inner]
1679    }
1680    blt::table configure $inner r* -resize none
1681    blt::table configure $inner r5 -resize expand
1682}
1683
1684itcl::body Rappture::NanovisViewer::BuildVolumeTab {} {
1685    foreach { key value } {
1686        light           40
1687        transp          50
1688        opacity         100
1689        thickness       350
1690    } {
1691        set _settings($this-$key) $value
1692    }
1693
1694    set inner [$itk_component(main) insert end \
1695        -title "Volume Settings" \
1696        -icon [Rappture::icon volume-on]]
1697    $inner configure -borderwidth 4
1698
1699    set fg [option get $itk_component(hull) font Font]
1700    #set bfg [option get $itk_component(hull) boldFont Font]
1701
1702    checkbutton $inner.vol -text "Show volume" -font $fg \
1703        -variable [itcl::scope _settings($this-volume)] \
1704        -command [itcl::code $this FixSettings volume]
1705    label $inner.shading -text "Shading:" -font $fg
1706
1707    label $inner.dim -text "Dim" -font $fg
1708    ::scale $inner.light -from 0 -to 100 -orient horizontal \
1709        -variable [itcl::scope _settings($this-light)] \
1710        -width 10 \
1711        -showvalue off -command [itcl::code $this FixSettings light]
1712    label $inner.bright -text "Bright" -font $fg
1713
1714    label $inner.fog -text "Fog" -font $fg
1715    ::scale $inner.transp -from 0 -to 100 -orient horizontal \
1716        -variable [itcl::scope _settings($this-transp)] \
1717        -width 10 \
1718        -showvalue off -command [itcl::code $this FixSettings transp]
1719    label $inner.plastic -text "Plastic" -font $fg
1720
1721    label $inner.clear -text "Clear" -font $fg
1722    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1723        -variable [itcl::scope _settings($this-opacity)] \
1724        -width 10 \
1725        -showvalue off -command [itcl::code $this FixSettings opacity]
1726    label $inner.opaque -text "Opaque" -font $fg
1727
1728    label $inner.thin -text "Thin" -font $fg
1729    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
1730        -variable [itcl::scope _settings($this-thickness)] \
1731        -width 10 \
1732        -showvalue off -command [itcl::code $this FixSettings thickness]
1733    label $inner.thick -text "Thick" -font $fg
1734
1735    blt::table $inner \
1736        0,0 $inner.vol -columnspan 4 -anchor w -pady 2 \
1737        1,0 $inner.shading -columnspan 4 -anchor w -pady {10 2} \
1738        2,0 $inner.dim -anchor e -pady 2 \
1739        2,1 $inner.light -columnspan 2 -pady 2 -fill x \
1740        2,3 $inner.bright -anchor w -pady 2 \
1741        3,0 $inner.fog -anchor e -pady 2 \
1742        3,1 $inner.transp -columnspan 2 -pady 2 -fill x \
1743        3,3 $inner.plastic -anchor w -pady 2 \
1744        4,0 $inner.clear -anchor e -pady 2 \
1745        4,1 $inner.opacity -columnspan 2 -pady 2 -fill x\
1746        4,3 $inner.opaque -anchor w -pady 2 \
1747        5,0 $inner.thin -anchor e -pady 2 \
1748        5,1 $inner.thickness -columnspan 2 -pady 2 -fill x\
1749        5,3 $inner.thick -anchor w -pady 2
1750
1751    blt::table configure $inner c0 c1 c3 r* -resize none
1752    blt::table configure $inner r6 -resize expand
1753}
1754
1755itcl::body Rappture::NanovisViewer::BuildCutplanesTab {} {
1756    set inner [$itk_component(main) insert end \
1757        -title "Cutplane Settings" \
1758        -icon [Rappture::icon cutbutton]]
1759    $inner configure -borderwidth 4
1760
1761    # X-value slicer...
1762    itk_component add xCutButton {
1763        Rappture::PushButton $inner.xbutton \
1764            -onimage [Rappture::icon x-cutplane] \
1765            -offimage [Rappture::icon x-cutplane] \
1766            -command [itcl::code $this FixSettings xcutplane] \
1767            -variable [itcl::scope _settings($this-xcutplane)]
1768    }
1769    Rappture::Tooltip::for $itk_component(xCutButton) \
1770        "Toggle the X cut plane on/off"
1771
1772    itk_component add xCutScale {
1773        ::scale $inner.xval -from 100 -to 0 \
1774            -width 10 -orient vertical -showvalue off \
1775            -borderwidth 1 -highlightthickness 0 \
1776            -command [itcl::code $this Slice move x] \
1777            -variable [itcl::scope _settings($this-xcutposition)]
1778    } {
1779        usual
1780        ignore -borderwidth -highlightthickness
1781    }
1782    # Set the default cutplane value before disabling the scale.
1783    $itk_component(xCutScale) set 50
1784    $itk_component(xCutScale) configure -state disabled
1785    Rappture::Tooltip::for $itk_component(xCutScale) \
1786        "@[itcl::code $this SlicerTip x]"
1787
1788    # Y-value slicer...
1789    itk_component add yCutButton {
1790        Rappture::PushButton $inner.ybutton \
1791            -onimage [Rappture::icon y-cutplane] \
1792            -offimage [Rappture::icon y-cutplane] \
1793            -command [itcl::code $this FixSettings ycutplane] \
1794            -variable [itcl::scope _settings($this-ycutplane)]
1795    }
1796    Rappture::Tooltip::for $itk_component(yCutButton) \
1797        "Toggle the Y cut plane on/off"
1798
1799    itk_component add yCutScale {
1800        ::scale $inner.yval -from 100 -to 0 \
1801            -width 10 -orient vertical -showvalue off \
1802            -borderwidth 1 -highlightthickness 0 \
1803            -command [itcl::code $this Slice move y] \
1804            -variable [itcl::scope _settings($this-ycutposition)]
1805    } {
1806        usual
1807        ignore -borderwidth -highlightthickness
1808    }
1809    Rappture::Tooltip::for $itk_component(yCutScale) \
1810        "@[itcl::code $this SlicerTip y]"
1811    # Set the default cutplane value before disabling the scale.
1812    $itk_component(yCutScale) set 50
1813    $itk_component(yCutScale) configure -state disabled
1814
1815    # Z-value slicer...
1816    itk_component add zCutButton {
1817        Rappture::PushButton $inner.zbutton \
1818            -onimage [Rappture::icon z-cutplane] \
1819            -offimage [Rappture::icon z-cutplane] \
1820            -command [itcl::code $this FixSettings zcutplane] \
1821            -variable [itcl::scope _settings($this-zcutplane)]
1822    }
1823    Rappture::Tooltip::for $itk_component(zCutButton) \
1824        "Toggle the Z cut plane on/off"
1825
1826    itk_component add zCutScale {
1827        ::scale $inner.zval -from 100 -to 0 \
1828            -width 10 -orient vertical -showvalue off \
1829            -borderwidth 1 -highlightthickness 0 \
1830            -command [itcl::code $this Slice move z] \
1831            -variable [itcl::scope _settings($this-zcutposition)]
1832    } {
1833        usual
1834        ignore -borderwidth -highlightthickness
1835    }
1836    $itk_component(zCutScale) set 50
1837    $itk_component(zCutScale) configure -state disabled
1838    #$itk_component(zCutScale) configure -state disabled
1839    Rappture::Tooltip::for $itk_component(zCutScale) \
1840        "@[itcl::code $this SlicerTip z]"
1841
1842    blt::table $inner \
1843        1,1 $itk_component(xCutButton) \
1844        1,2 $itk_component(yCutButton) \
1845        1,3 $itk_component(zCutButton) \
1846        0,1 $itk_component(xCutScale) \
1847        0,2 $itk_component(yCutScale) \
1848        0,3 $itk_component(zCutScale)
1849
1850    blt::table configure $inner r0 r1 c* -resize none
1851    blt::table configure $inner r2 c4 -resize expand
1852    blt::table configure $inner c0 -width 2
1853    blt::table configure $inner c1 c2 c3 -padx 2
1854}
1855
1856itcl::body Rappture::NanovisViewer::BuildCameraTab {} {
1857    set inner [$itk_component(main) insert end \
1858        -title "Camera Settings" \
1859        -icon [Rappture::icon camera]]
1860    $inner configure -borderwidth 4
1861
1862    set labels { phi theta psi pan-x pan-y zoom }
1863    set row 0
1864    foreach tag $labels {
1865        label $inner.${tag}label -text $tag -font "Arial 9"
1866        entry $inner.${tag} -font "Arial 9"  -bg white \
1867            -textvariable [itcl::scope _settings($this-$tag)]
1868        bind $inner.${tag} <KeyPress-Return> \
1869            [itcl::code $this camera set ${tag}]
1870        blt::table $inner \
1871            $row,0 $inner.${tag}label -anchor e -pady 2 \
1872            $row,1 $inner.${tag} -anchor w -pady 2
1873        blt::table configure $inner r$row -resize none
1874        incr row
1875    }
1876
1877    itk_component add orientation {
1878        Rappture::Combobox $inner.orientation -width 10 -editable no
1879    }
1880    $inner.orientation choices insert end \
1881        "front"    "0 90 0" \
1882        "back"   "180 90 0" \
1883        "top"     "0 0 0"  \
1884        "bottom"     "0 180 0"  \
1885        "left"     "270 90 0 "  \
1886        "right"     "90 90 0"  \
1887        "default"  "45 45 0"
1888    $itk_component(orientation) value "default"
1889    bind $inner.orientation <<Value>> [itcl::code $this SetOrientation]
1890
1891    blt::table configure $inner c0 c1 -resize none
1892    blt::table configure $inner c2 -resize expand
1893    blt::table configure $inner r$row -resize expand
1894}
1895
1896
1897# ----------------------------------------------------------------------
1898# USAGE: Slice move x|y|z <newval>
1899#
1900# Called automatically when the user drags the slider to move the
1901# cut plane that slices 3D data.  Gets the current value from the
1902# slider and moves the cut plane to the appropriate point in the
1903# data set.
1904# ----------------------------------------------------------------------
1905itcl::body Rappture::NanovisViewer::Slice {option args} {
1906    switch -- $option {
1907        move {
1908            if {[llength $args] != 2} {
1909                error "wrong # args: should be \"Slice move x|y|z newval\""
1910            }
1911            set axis [lindex $args 0]
1912            set newval [lindex $args 1]
1913
1914            set newpos [expr {0.01*$newval}]
1915            set vols [CurrentVolumes -cutplanes]
1916            set vol [lindex $vols 0]
1917            SendCmd "cutplane position $newpos $axis $vol"
1918        }
1919        default {
1920            error "bad option \"$option\": should be axis, move, or volume"
1921        }
1922    }
1923}
1924
1925# ----------------------------------------------------------------------
1926# USAGE: SlicerTip <axis>
1927#
1928# Used internally to generate a tooltip for the x/y/z slicer controls.
1929# Returns a message that includes the current slicer value.
1930# ----------------------------------------------------------------------
1931itcl::body Rappture::NanovisViewer::SlicerTip {axis} {
1932    set val [$itk_component(${axis}CutScale) get]
1933#    set val [expr {0.01*($val-50)
1934#        *($_limits(${axis}max)-$_limits(${axis}min))
1935#          + 0.5*($_limits(${axis}max)+$_limits(${axis}min))}]
1936    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
1937}
1938
1939
1940itcl::body Rappture::NanovisViewer::DoResize {} {
1941    SendCmd "screen $_width $_height"
1942    set _resizePending 0
1943}
1944
1945itcl::body Rappture::NanovisViewer::EventuallyResize { w h } {
1946    set _width $w
1947    set _height $h
1948    if { !$_resizePending } {
1949        $_dispatcher event -idle !resize
1950        set _resizePending 1
1951    }
1952}
1953
1954itcl::body Rappture::NanovisViewer::EventuallyResizeLegend {} {
1955    if { !$_resizeLegendPending } {
1956        $_dispatcher event -idle !legend
1957        set _resizeLegendPending 1
1958    }
1959}
1960
1961
1962#  camera --
1963#
1964itcl::body Rappture::NanovisViewer::camera {option args} {
1965    switch -- $option {
1966        "show" {
1967            puts [array get _view]
1968        }
1969        "set" {
1970            set who [lindex $args 0]
1971            set x $_settings($this-$who)
1972            set code [catch { string is double $x } result]
1973            if { $code != 0 || !$result } {
1974                set _settings($this-$who) $_view($who)
1975                return
1976            }
1977            switch -- $who {
1978                "pan-x" - "pan-y" {
1979                    set _view($who) $_settings($this-$who)
1980                    PanCamera
1981                }
1982                "phi" - "theta" - "psi" {
1983                    set _view($who) $_settings($this-$who)
1984                    set xyz [Euler2XYZ $_view(theta) $_view(phi) $_view(psi)]
1985                    SendCmd "camera angle $xyz"
1986                }
1987                "zoom" {
1988                    set _view($who) $_settings($this-$who)
1989                    SendCmd "camera zoom $_view(zoom)"
1990                }
1991            }
1992        }
1993    }
1994}
1995
1996itcl::body Rappture::NanovisViewer::GetVolumeInfo { w } {
1997    set flowobj ""
1998    foreach key [array names _obj2flow] {
1999        set flowobj $_obj2flow($key)
2000        break
2001    }
2002    if { $flowobj == "" } {
2003        return
2004    }
2005    if { [winfo exists $w.frame] } {
2006        destroy $w.frame
2007    }
2008    set inner [frame $w.frame]
2009    blt::table $w \
2010        5,0 $inner -fill both -columnspan 2 -anchor nw
2011    array set hints [$dataobj hints]
2012
2013    label $inner.volumes -text "Volumes" -font "Arial 9 bold"
2014    blt::table $inner \
2015        1,0 $inner.volumes  -anchor w \
2016    blt::table configure $inner c0 c1 -resize none
2017    blt::table configure $inner c2 -resize expand
2018
2019    set row 3
2020    set volumes [get]
2021    if { [llength $volumes] > 0 } {
2022        blt::table $inner $row,0 $inner.volumes  -anchor w
2023        incr row
2024    }
2025    foreach vol $volumes {
2026        array unset info
2027        array set info $vol
2028        set name $info(name)
2029        if { ![info exists _settings($this-volume-$name)] } {
2030            set _settings($this-volume-$name) $info(hide)
2031        }
2032        checkbutton $inner.vol$row -text $info(label) \
2033            -variable [itcl::scope _settings($this-volume-$name)] \
2034            -onvalue 0 -offvalue 1 \
2035            -command [itcl::code $this volume $key $name] \
2036            -font "Arial 9"
2037        Rappture::Tooltip::for $inner.vol$row $info(description)
2038        blt::table $inner $row,0 $inner.vol$row -anchor w
2039        if { !$_settings($this-volume-$name) } {
2040            $inner.vol$row select
2041        }
2042        incr row
2043    }
2044    blt::table configure $inner r* -resize none
2045    blt::table configure $inner r$row -resize expand
2046    blt::table configure $inner c3 -resize expand
2047    event generate [winfo parent [winfo parent $w]] <Configure>
2048}
2049
2050itcl::body Rappture::NanovisViewer::volume { tag name } {
2051    set bool $_settings($this-volume-$name)
2052    SendCmd "volume statue $bool $name"
2053}
2054
2055
2056itcl::body Rappture::NanovisViewer::SetOrientation {} {
2057    set angles [$itk_component(orientation) value]
2058    foreach name { theta phi psi } angle $angles {
2059        set _view($name) $angle
2060    }
2061    set xyz [Euler2XYZ $_view(theta) $_view(phi) $_view(psi)]
2062    SendCmd "camera angle $xyz"
2063}
Note: See TracBrowser for help on using the repository browser.