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

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

These was are all related to the omenwire example.

o Added validity test for fields, meshes, clouds, and unirect2ds. There is

now a "isvalid" method that viewers should use to verify that the data object
can be plotted.

In some cases with fields this means that the widget won't even be created.
The resultviewer tests for the dimensionality which is by default 0.

o Thanks to Leif for pointing this out, it's not enough to check if the field

is valid. Individual components of the field may be invalid. Added check so
that viewers are never passed the names of invalid field components.

o Changed many "error" commands to just print to stderr and tolerantly deal

with the error.

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