source: branches/1.3/gui/scripts/nanovisviewer.tcl @ 3844

Last change on this file since 3844 was 3844, checked in by ldelgass, 11 years ago

Sync with trunk. Branch now differs only from trunk by r3722 (branch is version
1.3, trunk is version 1.4)

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