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

Last change on this file since 4042 was 3948, checked in by gah, 11 years ago

don't display component dropdown if there's only one component.

File size: 89.6 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 parameters {title args} {
79        # do nothing
80    }
81    public method scale {args}
82    public method updateTransferFunctions {}
83
84
85    # The following methods are only used by this class.
86    private method AdjustSetting {what {value ""}}
87    private method BuildCameraTab {}
88    private method BuildCutplanesTab {}
89    private method BuildViewTab {}
90    private method BuildVolumeComponents {}
91    private method BuildVolumeTab {}
92    private method ComputeAlphamap { cname }
93    private method ComputeTransferFunction { cname }
94    private method Connect {}
95    private method CurrentDatasets {{what -all}}
96    private method Disconnect {}
97    private method DoResize {}
98    private method DrawLegend { cname }
99    private method EventuallyRedrawLegend { }
100    private method EventuallyResize { w h }
101    private method FixLegend {}
102    private method GetAlphamap { cname color }
103    private method GetColormap { cname color }
104    private method GetDatasetsWithComponent { cname }
105    private method GetVolumeInfo { w }
106    private method HideAllMarkers {}
107    private method InitComponentSettings { cname }
108    private method InitSettings { args }
109    private method NameToAlphamap { name }
110    private method NameTransferFunction { dataobj comp }
111    private method Pan {option x y}
112    private method PanCamera {}
113    private method ParseLevelsOption { cname levels }
114    private method ParseMarkersOption { cname markers }
115    private method Rebuild {}
116    private method ReceiveData { args }
117    private method ReceiveImage { args }
118    private method ReceiveLegend { tf vmin vmax size }
119    private method ResetColormap { cname color }
120    private method Rotate {option x y}
121    private method SendTransferFunctions {}
122    private method SetOrientation { side }
123    private method Slice {option args}
124    private method SlicerTip {axis}
125    private method SwitchComponent { cname }
126    private method Zoom {option}
127    private method ToggleVolume { tag name }
128
129    private variable _arcball ""
130
131    private variable _dlist ""     ;# list of data objects
132    private variable _obj2ovride   ;# maps dataobj => style override
133    private variable _serverDatasets   ;# contains all the dataobj-component
134                                   ;# to volumes in the server
135    private variable _recvdDatasets;    # list of data objs to send to server
136    private variable _dataset2style;    # maps dataobj-component to transfunc
137    private variable _style2datasets;   # maps tf back to list of
138                                        # dataobj-components using the tf.
139
140    private variable _reset 1;          # Connection to server has been reset.
141    private variable _click;            # Info used for rotate operations.
142    private variable _limits;           # Autoscale min/max for all axes
143    private variable _view;             # View params for 3D view
144    private variable _parsedFunction
145    private variable _transferFunctionEditors;# Array of isosurface level values 0..1
146    private variable  _settings
147    private variable _first "" ;        # This is the topmost volume.
148    private variable _current "";       # Currently selected component
149    private variable _volcomponents   ; # Array of components found
150    private variable _componentsList   ; # Array of components found
151    private variable _cname2style
152    private variable _cname2transferFunction
153    private variable _cname2defaultcolormap
154    private variable _cname2defaultalphamap
155
156    common _downloadPopup          ;# download options from popup
157    private common _hardcopy
158    private variable _width 0
159    private variable _height 0
160    private variable _resizePending 0
161    private variable _resizeLegendPending 0
162}
163
164itk::usual NanovisViewer {
165    keep -background -foreground -cursor -font
166    keep -plotbackground -plotforeground
167}
168
169# ----------------------------------------------------------------------
170# CONSTRUCTOR
171# ----------------------------------------------------------------------
172itcl::body Rappture::NanovisViewer::constructor {hostlist args} {
173    set _serverType "nanovis"
174
175    # Draw legend event
176    $_dispatcher register !legend
177    $_dispatcher dispatch $this !legend "[itcl::code $this FixLegend]; list"
178
179    # Send transfer functions event
180    $_dispatcher register !send_transfunc
181    $_dispatcher dispatch $this !send_transfunc \
182        "[itcl::code $this SendTransferFunctions]; list"
183
184    # Rebuild event
185    $_dispatcher register !rebuild
186    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
187
188    # Resize event
189    $_dispatcher register !resize
190    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
191
192    #
193    # Populate parser with commands handle incoming requests
194    #
195    $_parser alias image [itcl::code $this ReceiveImage]
196    $_parser alias legend [itcl::code $this ReceiveLegend]
197    $_parser alias data [itcl::code $this ReceiveData]
198
199    # Initialize the view to some default parameters.
200    array set _view {
201        qw      0.853553
202        qx      -0.353553
203        qy      0.353553
204        qz      0.146447
205        zoom    1.0
206        xpan    0
207        ypan    0
208    }
209    set _arcball [blt::arcball create 100 100]
210    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
211    $_arcball quaternion $q
212
213    set _limits(v) [list 0.0 1.0]
214    set _reset 1
215
216    array set _settings [subst {
217        $this-ambient           60
218        $this-colormap          default
219        $this-cutplaneVisible   0
220        $this-diffuse           40
221        $this-light2side        1
222        $this-opacity           100
223        $this-qw                $_view(qw)
224        $this-qx                $_view(qx)
225        $this-qy                $_view(qy)
226        $this-qz                $_view(qz)
227        $this-specularExponent  90
228        $this-specularLevel     30
229        $this-thickness         350
230        $this-transp            50
231        $this-volume            1
232        $this-volumeVisible     1
233        $this-xcutplane         1
234        $this-xcutposition      0
235        $this-xpan              $_view(xpan)
236        $this-ycutplane         1
237        $this-ycutposition      0
238        $this-ypan              $_view(ypan)
239        $this-zcutplane         1
240        $this-zcutposition      0
241        $this-zoom              $_view(zoom)   
242    }]
243
244    itk_component add 3dview {
245        label $itk_component(plotarea).view -image $_image(plot) \
246            -highlightthickness 0 -borderwidth 0
247    } {
248        usual
249        ignore -highlightthickness -borderwidth  -background
250    }
251    bind $itk_component(3dview) <Control-F1> [itcl::code $this ToggleConsole]
252
253    set f [$itk_component(main) component controls]
254    itk_component add reset {
255        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
256            -highlightthickness 0 \
257            -image [Rappture::icon reset-view] \
258            -command [itcl::code $this Zoom reset]
259    } {
260        usual
261        ignore -highlightthickness
262    }
263    pack $itk_component(reset) -side top -padx 2 -pady 2
264    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
265
266    itk_component add zoomin {
267        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
268            -highlightthickness 0 \
269            -image [Rappture::icon zoom-in] \
270            -command [itcl::code $this Zoom in]
271    } {
272        usual
273        ignore -highlightthickness
274    }
275    pack $itk_component(zoomin) -side top -padx 2 -pady 2
276    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
277
278    itk_component add zoomout {
279        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
280            -highlightthickness 0 \
281            -image [Rappture::icon zoom-out] \
282            -command [itcl::code $this Zoom out]
283    } {
284        usual
285        ignore -highlightthickness
286    }
287    pack $itk_component(zoomout) -side top -padx 2 -pady 2
288    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
289
290    itk_component add volume {
291        Rappture::PushButton $f.volume \
292            -onimage [Rappture::icon volume-on] \
293            -offimage [Rappture::icon volume-off] \
294            -command [itcl::code $this AdjustSetting volume] \
295            -variable [itcl::scope _settings($this-volume)]
296    }
297    $itk_component(volume) select
298    Rappture::Tooltip::for $itk_component(volume) \
299        "Toggle the volume cloud on/off"
300    pack $itk_component(volume) -padx 2 -pady 2
301
302    itk_component add cutplane {
303        Rappture::PushButton $f.cutplane \
304            -onimage [Rappture::icon cutbutton] \
305            -offimage [Rappture::icon cutbutton] \
306            -variable [itcl::scope _settings($this-cutplaneVisible)] \
307            -command [itcl::code $this AdjustSetting cutplaneVisible]
308    }
309    Rappture::Tooltip::for $itk_component(cutplane) \
310        "Show/Hide cutplanes"
311    pack $itk_component(cutplane) -padx 2 -pady 2
312
313    if { [catch {
314        BuildViewTab
315        BuildVolumeTab
316        BuildCutplanesTab
317        BuildCameraTab
318    } errs] != 0 } {
319        global errorInfo
320        puts stderr "errs=$errs errorInfo=$errorInfo"
321    }
322
323    # Legend
324
325    set _image(legend) [image create photo]
326    itk_component add legend {
327        canvas $itk_component(plotarea).legend -height 50 -highlightthickness 0
328    } {
329        usual
330        ignore -highlightthickness
331        rename -background -plotbackground plotBackground Background
332    }
333    bind $itk_component(legend) <Configure> \
334        [itcl::code $this EventuallyRedrawLegend]
335
336    # Hack around the Tk panewindow.  The problem is that the requested
337    # size of the 3d view isn't set until an image is retrieved from
338    # the server.  So the panewindow uses the tiny size.
339    set w 10000
340    pack forget $itk_component(3dview)
341    blt::table $itk_component(plotarea) \
342        0,0 $itk_component(3dview) -fill both -reqwidth $w \
343        1,0 $itk_component(legend) -fill x
344    blt::table configure $itk_component(plotarea) r1 -resize none
345
346    # Bindings for rotation via mouse
347    bind $itk_component(3dview) <ButtonPress-1> \
348        [itcl::code $this Rotate click %x %y]
349    bind $itk_component(3dview) <B1-Motion> \
350        [itcl::code $this Rotate drag %x %y]
351    bind $itk_component(3dview) <ButtonRelease-1> \
352        [itcl::code $this Rotate release %x %y]
353    bind $itk_component(3dview) <Configure> \
354        [itcl::code $this EventuallyResize %w %h]
355
356    # Bindings for panning via mouse
357    bind $itk_component(3dview) <ButtonPress-2> \
358        [itcl::code $this Pan click %x %y]
359    bind $itk_component(3dview) <B2-Motion> \
360        [itcl::code $this Pan drag %x %y]
361    bind $itk_component(3dview) <ButtonRelease-2> \
362        [itcl::code $this Pan release %x %y]
363
364    # Bindings for panning via keyboard
365    bind $itk_component(3dview) <KeyPress-Left> \
366        [itcl::code $this Pan set -10 0]
367    bind $itk_component(3dview) <KeyPress-Right> \
368        [itcl::code $this Pan set 10 0]
369    bind $itk_component(3dview) <KeyPress-Up> \
370        [itcl::code $this Pan set 0 -10]
371    bind $itk_component(3dview) <KeyPress-Down> \
372        [itcl::code $this Pan set 0 10]
373    bind $itk_component(3dview) <Shift-KeyPress-Left> \
374        [itcl::code $this Pan set -2 0]
375    bind $itk_component(3dview) <Shift-KeyPress-Right> \
376        [itcl::code $this Pan set 2 0]
377    bind $itk_component(3dview) <Shift-KeyPress-Up> \
378        [itcl::code $this Pan set 0 -2]
379    bind $itk_component(3dview) <Shift-KeyPress-Down> \
380        [itcl::code $this Pan set 0 2]
381
382    # Bindings for zoom via keyboard
383    bind $itk_component(3dview) <KeyPress-Prior> \
384        [itcl::code $this Zoom out]
385    bind $itk_component(3dview) <KeyPress-Next> \
386        [itcl::code $this Zoom in]
387
388    bind $itk_component(3dview) <Enter> "focus $itk_component(3dview)"
389
390    if {[string equal "x11" [tk windowingsystem]]} {
391        # Bindings for zoom via mouse
392        bind $itk_component(3dview) <4> [itcl::code $this Zoom out]
393        bind $itk_component(3dview) <5> [itcl::code $this Zoom in]
394    }
395
396    set _image(download) [image create photo]
397
398    eval itk_initialize $args
399
400    Connect
401}
402
403# ----------------------------------------------------------------------
404# DESTRUCTOR
405# ----------------------------------------------------------------------
406itcl::body Rappture::NanovisViewer::destructor {} {
407    $_dispatcher cancel !rebuild
408    $_dispatcher cancel !send_transfunc
409    $_dispatcher cancel !resize
410    image delete $_image(plot)
411    image delete $_image(legend)
412    image delete $_image(download)
413    foreach name [array names _transferFunctionEditors] {
414        itcl::delete object $_transferFunctionEditors($cname)
415    }
416    catch { blt::arcball destroy $_arcball }
417    array unset _settings $this-*
418}
419
420# ----------------------------------------------------------------------
421# USAGE: add <dataobj> ?<settings>?
422#
423# Clients use this to add a data object to the plot.  The optional
424# <settings> are used to configure the plot.  Allowed settings are
425# -color, -brightness, -width, -linestyle, and -raise.
426# ----------------------------------------------------------------------
427itcl::body Rappture::NanovisViewer::add {dataobj {settings ""}} {
428    if { ![$dataobj isvalid] } {
429        return;                         # Object doesn't contain valid data.
430    }
431    array set params {
432        -color auto
433        -width 1
434        -linestyle solid
435        -brightness 0
436        -raise 0
437        -description ""
438        -param ""
439    }
440    array set params $settings
441
442    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
443        # can't handle -autocolors yet
444        set params(-color) black
445    }
446    set pos [lsearch -exact $_dlist $dataobj]
447    if {$pos < 0} {
448        lappend _dlist $dataobj
449        set _obj2ovride($dataobj-color) $params(-color)
450        set _obj2ovride($dataobj-width) $params(-width)
451        set _obj2ovride($dataobj-raise) $params(-raise)
452        $_dispatcher event -idle !rebuild
453    }
454}
455
456# ----------------------------------------------------------------------
457# USAGE: get ?-objects?
458# USAGE: get ?-image 3dview|legend?
459#
460# Clients use this to query the list of objects being plotted, in
461# order from bottom to top of this result.  The optional "-image"
462# flag can also request the internal images being shown.
463# ----------------------------------------------------------------------
464itcl::body Rappture::NanovisViewer::get {args} {
465    if {[llength $args] == 0} {
466        set args "-objects"
467    }
468
469    set op [lindex $args 0]
470    switch -- $op {
471      -objects {
472        # put the dataobj list in order according to -raise options
473        set dlist $_dlist
474        foreach obj $dlist {
475            if {[info exists _obj2ovride($obj-raise)] && $_obj2ovride($obj-raise)} {
476                set i [lsearch -exact $dlist $obj]
477                if {$i >= 0} {
478                    set dlist [lreplace $dlist $i $i]
479                    lappend dlist $obj
480                }
481            }
482        }
483        return $dlist
484      }
485      -image {
486        if {[llength $args] != 2} {
487            error "wrong # args: should be \"get -image 3dview|legend\""
488        }
489        switch -- [lindex $args end] {
490            3dview {
491                return $_image(plot)
492            }
493            legend {
494                return $_image(legend)
495            }
496            default {
497                error "bad image name \"[lindex $args end]\": should be 3dview or legend"
498            }
499        }
500      }
501      default {
502        error "bad option \"$op\": should be -objects or -image"
503      }
504    }
505}
506
507# ----------------------------------------------------------------------
508# USAGE: delete ?<dataobj1> <dataobj2> ...?
509#
510#       Clients use this to delete a dataobj from the plot.  If no dataobjs
511#       are specified, then all dataobjs are deleted.  No data objects are
512#       deleted.  They are only removed from the display list.
513#
514# ----------------------------------------------------------------------
515itcl::body Rappture::NanovisViewer::delete {args} {
516    if {[llength $args] == 0} {
517        set args $_dlist
518    }
519    # Delete all specified dataobjs
520    set changed 0
521    foreach dataobj $args {
522        set pos [lsearch -exact $_dlist $dataobj]
523        if { $pos >= 0 } {
524            set _dlist [lreplace $_dlist $pos $pos]
525            array unset _obj2ovride $dataobj-*
526            array unset _dataset2style $dataobj-*
527            set changed 1
528        }
529    }
530    # If anything changed, then rebuild the plot
531    if {$changed} {
532        $_dispatcher event -idle !rebuild
533    }
534}
535
536# ----------------------------------------------------------------------
537# USAGE: scale ?<data1> <data2> ...?
538#
539# Sets the default limits for the overall plot according to the
540# limits of the data for all of the given <data> objects.  This
541# accounts for all objects--even those not showing on the screen.
542# Because of this, the limits are appropriate for all objects as
543# the user scans through data in the ResultSet viewer.
544# ----------------------------------------------------------------------
545itcl::body Rappture::NanovisViewer::scale {args} {
546    array set style {
547        -color BCGYR
548        -levels 6
549        -opacity 1.0
550        -markers ""
551    }
552    array unset _limits
553    array unset _volcomponents
554    foreach dataobj $args {
555        if { ![$dataobj isvalid] } {
556            continue;                     # Object doesn't contain valid data.
557        }
558        foreach cname [$dataobj components] {
559            if { ![info exists _volcomponents($cname)] } {
560                lappend _componentsList $cname
561                array set style [lindex [$dataobj components -style $cname] 0]
562                set cmap [ColorsToColormap $style(-color)]
563                set _cname2defaultcolormap($cname) $cmap
564                set _settings($cname-colormap) $style(-color)
565            }
566            lappend _volcomponents($cname) $dataobj-$cname
567            array unset limits
568            array set limits [$dataobj valueLimits $cname]
569            set _limits($cname) $limits(v)
570        }
571        foreach axis {x y z v} {
572            foreach { min max } [$dataobj limits $axis] break
573            if {"" != $min && "" != $max} {
574                if { ![info exists _limits($axis)] } {
575                    set _limits($axis) [list $min $max]
576                } else {
577                    foreach {amin amax} $_limits($axis) break
578                    if {$min < $amin} {
579                        set amin $min
580                    }
581                    if {$max > $amax} {
582                        set amax $max
583                    }
584                    set _limits($axis) [list $amin $amax]
585                }
586            }
587        }
588    }
589    BuildVolumeComponents
590}
591
592# ----------------------------------------------------------------------
593# USAGE: download coming
594# USAGE: download controls <downloadCommand>
595# USAGE: download now
596#
597# Clients use this method to create a downloadable representation
598# of the plot.  Returns a list of the form {ext string}, where
599# "ext" is the file extension (indicating the type of data) and
600# "string" is the data itself.
601# ----------------------------------------------------------------------
602itcl::body Rappture::NanovisViewer::download {option args} {
603    switch $option {
604        coming {
605            if {[catch {
606                blt::winop snap $itk_component(plotarea) $_image(download)
607            }]} {
608                $_image(download) configure -width 1 -height 1
609                $_image(download) put #000000
610            }
611        }
612        controls {
613            # no controls for this download yet
614            return ""
615        }
616        now {
617            # Get the image data (as base64) and decode it back to binary.
618            # This is better than writing to temporary files.  When we switch
619            # to the BLT picture image it won't be necessary to decode the
620            # image data.
621            if { [image width $_image(plot)] > 0 &&
622                 [image height $_image(plot)] > 0 } {
623                set bytes [$_image(plot) data -format "jpeg -quality 100"]
624                set bytes [Rappture::encoding::decode -as b64 $bytes]
625                return [list .jpg $bytes]
626            }
627            return ""
628        }
629        default {
630            error "bad option \"$option\": should be coming, controls, now"
631        }
632    }
633}
634
635# ----------------------------------------------------------------------
636# USAGE: Connect ?<host:port>,<host:port>...?
637#
638# Clients use this method to establish a connection to a new
639# server, or to reestablish a connection to the previous server.
640# Any existing connection is automatically closed.
641# ----------------------------------------------------------------------
642itcl::body Rappture::NanovisViewer::Connect {} {
643    set _hosts [GetServerList "nanovis"]
644    if { "" == $_hosts } {
645        return 0
646    }
647    set _reset 1
648    set result [VisViewer::Connect $_hosts]
649    if { $result } {
650        if { $_reportClientInfo }  {
651            # Tell the server the viewer, hub, user and session.
652            # Do this immediately on connect before buffing any commands
653            global env
654
655            set info {}
656            set user "???"
657            if { [info exists env(USER)] } {
658                set user $env(USER)
659            }
660            set session "???"
661            if { [info exists env(SESSION)] } {
662                set session $env(SESSION)
663            }
664            lappend info "hub" [exec hostname]
665            lappend info "client" "nanovisviewer"
666            lappend info "user" $user
667            lappend info "session" $session
668            SendCmd "clientinfo [list $info]"
669        }
670
671        set w [winfo width $itk_component(3dview)]
672        set h [winfo height $itk_component(3dview)]
673        EventuallyResize $w $h
674    }
675    return $result
676}
677
678#
679# isconnected --
680#
681#       Indicates if we are currently connected to the visualization server.
682#
683itcl::body Rappture::NanovisViewer::isconnected {} {
684    return [VisViewer::IsConnected]
685}
686
687#
688# disconnect --
689#
690itcl::body Rappture::NanovisViewer::disconnect {} {
691    Disconnect
692}
693
694#
695# Disconnect --
696#
697#       Clients use this method to disconnect from the current rendering
698#       server.
699#
700itcl::body Rappture::NanovisViewer::Disconnect {} {
701    VisViewer::Disconnect
702
703    # disconnected -- no more data sitting on server
704    array unset _serverDatasets
705}
706
707# ----------------------------------------------------------------------
708# USAGE: SendTransferFunctions
709# ----------------------------------------------------------------------
710itcl::body Rappture::NanovisViewer::SendTransferFunctions {} {
711    if 0 {
712    if { $_first == "" } {
713        puts stderr "first not set"
714        return
715    }
716
717    foreach tag [CurrentDatasets] {
718        if { ![info exists _serverDatasets($tag)] || !$_serverDatasets($tag) } {
719            # The volume hasn't reached the server yet.  How did we get
720            # here?
721            puts stderr "Don't have $tag in _serverDatasets"
722            continue
723        }
724        if { ![info exists _dataset2style($tag)] } {
725            puts stderr "don't have style for volume $tag"
726            continue;                        # How does this happen?
727        }
728        foreach {dataobj cname} [split $tag -] break
729        set cname $_dataset2style($tag)
730
731        ComputeTransferFunction $cname
732        SendCmd "volume shading transfunc $cname $tag"
733    }
734    }
735    foreach cname [array names _volcomponents] {
736        ComputeTransferFunction $cname
737    }
738    FixLegend
739}
740
741# ----------------------------------------------------------------------
742# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
743#
744# Invoked automatically whenever the "image" command comes in from
745# the rendering server.  Indicates that binary image data with the
746# specified <size> will follow.
747# ----------------------------------------------------------------------
748itcl::body Rappture::NanovisViewer::ReceiveImage { args } {
749    array set info {
750        -token "???"
751        -bytes 0
752        -type image
753    }
754    array set info $args
755    set bytes [ReceiveBytes $info(-bytes)]
756    ReceiveEcho <<line "<read $info(-bytes) bytes"
757    if { $info(-type) == "image" } {
758        ReceiveEcho "for [image width $_image(plot)]x[image height $_image(plot)] image>"       
759        $_image(plot) configure -data $bytes
760    } elseif { $info(-type) == "print" } {
761        set tag $this-print-$info(-token)
762        set _hardcopy($tag) $bytes
763    }
764}
765
766#
767# DrawLegend --
768#
769itcl::body Rappture::NanovisViewer::DrawLegend { cname } {
770    set c $itk_component(legend)
771    set w [winfo width $c]
772    set h [winfo height $c]
773    set lx 10
774    set ly [expr {$h - 1}]
775    if {"" == [$c find withtag transfunc]} {
776        $c create image 10 10 -anchor nw \
777            -image $_image(legend) -tags transfunc
778        $c create text $lx $ly -anchor sw \
779            -fill $itk_option(-plotforeground) -tags "limits text vmin"
780        $c create text [expr {$w-$lx}] $ly -anchor se \
781            -fill $itk_option(-plotforeground) -tags "limits text vmax"
782        $c create text [expr {$w/2}] $ly -anchor s \
783            -fill $itk_option(-plotforeground) -tags "limits text title"
784        $c lower transfunc
785    }
786
787    # Display the markers used by the current transfer function.
788    HideAllMarkers
789    $_transferFunctionEditors($cname) showMarkers $_limits($cname)
790
791    foreach {min max} $_limits($cname) break
792    $c itemconfigure vmin -text [format %.2g $min]
793    $c coords vmin $lx $ly
794
795    $c itemconfigure vmax -text [format %.2g $max]
796    $c coords vmax [expr {$w-$lx}] $ly
797
798    set title [$_first hints label]
799    set units [$_first hints units]
800    if { $units != "" } {
801        set title "$title ($units)"
802    }
803    $c itemconfigure title -text $title
804    $c coords title [expr {$w/2}] $ly
805
806    # The colormap may have changed. Resync the slicers with the colormap.
807    set datasets [CurrentDatasets -cutplanes]
808
809    # Adjust the cutplane for only the first component in the topmost volume
810    # (i.e. the first volume designated in the field).
811    set tag [lindex $datasets 0]
812    foreach axis {x y z} {
813        # Turn off cutplanes for all volumes
814        SendCmd "cutplane state 0 $axis"
815        if { $_settings($this-${axis}cutplane) } {
816            # Turn on cutplane for this particular volume and set the position
817            SendCmd "cutplane state 1 $axis $tag"
818            set pos [expr {0.01*$_settings($this-${axis}cutposition)}]
819            SendCmd "cutplane position $pos $axis $tag"
820        }
821    }
822}
823
824#
825#
826# ReceiveLegend --
827#
828#       The procedure is the response from the render server to each "legend"
829#       command.  The server sends back a "legend" command invoked our
830#       the slave interpreter.  The purpose is to collect data of the image
831#       representing the legend in the canvas.  In addition, the
832#       active transfer function is displayed.
833#
834#
835itcl::body Rappture::NanovisViewer::ReceiveLegend { cname vmin vmax size } {
836    if { ![isconnected] } {
837        return
838    }
839    set bytes [ReceiveBytes $size]
840    $_image(legend) configure -data $bytes
841    ReceiveEcho <<line "<read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
842
843    DrawLegend $_current
844}
845
846#
847# ReceiveData --
848#
849#       The procedure is the response from the render server to each "data
850#       follows" command.  The server sends back a "data" command invoked our
851#       the slave interpreter.  The purpose is to collect the min/max of the
852#       volume sent to the render server.  Since the client (nanovisviewer)
853#       doesn't parse 3D data formats, we rely on the server (nanovis) to
854#       tell us what the limits are.  Once we've received the limits to all
855#       the data we've sent (tracked by _recvdDatasets) we can then determine
856#       what the transfer functions are for these volumes.
857#
858#
859#       Note: There is a considerable tradeoff in having the server report
860#             back what the data limits are.  It means that much of the code
861#             having to do with transfer-functions has to wait for the data
862#             to come back, since the isomarkers are calculated based upon
863#             the data limits.  The client code is much messier because of
864#             this.  The alternative is to parse any of the 3D formats on the
865#             client side.
866#
867itcl::body Rappture::NanovisViewer::ReceiveData { args } {
868    if { ![isconnected] } {
869        return
870    }
871
872    # Arguments from server are name value pairs. Stuff them in an array.
873    array set info $args
874
875    set tag $info(tag)
876    set parts [split $tag -]
877
878    #
879    # Volumes don't exist until we're told about them.
880    #
881    set dataobj [lindex $parts 0]
882    set _serverDatasets($tag) 1
883    if { $_settings($this-volume) && $dataobj == $_first } {
884        SendCmd "volume state 1 $tag"
885    }
886    set _limits($tag) [list $info(min)  $info(max)]
887    set _limits(v)    [list $info(vmin) $info(vmax)]
888
889    unset _recvdDatasets($tag)
890    if { [array size _recvdDatasets] == 0 } {
891        updateTransferFunctions
892    }
893}
894
895# ----------------------------------------------------------------------
896# USAGE: Rebuild
897#
898# Called automatically whenever something changes that affects the
899# data in the widget.  Clears any existing data and rebuilds the
900# widget to display new data.
901# ----------------------------------------------------------------------
902itcl::body Rappture::NanovisViewer::Rebuild {} {
903    set w [winfo width $itk_component(3dview)]
904    set h [winfo height $itk_component(3dview)]
905    if { $w < 2 || $h < 2 } {
906        $_dispatcher event -idle !rebuild
907        return
908    }
909
910    # Turn on buffering of commands to the server.  We don't want to
911    # be preempted by a server disconnect/reconnect (which automatically
912    # generates a new call to Rebuild).   
913    StartBufferingCommands
914
915    if { $_width != $w || $_height != $h || $_reset } {
916        set _width $w
917        set _height $h
918        $_arcball resize $w $h
919        DoResize
920    }
921    foreach dataobj [get] {
922        foreach cname [$dataobj components] {
923            set tag $dataobj-$cname
924            if { ![info exists _serverDatasets($tag)] } {
925                # Send the data as one huge base64-encoded mess -- yuck!
926                if { [$dataobj type] == "dx" } {
927                    if { ![$dataobj isvalid] } {
928                        puts stderr "??? $dataobj is invalid"
929                    }
930                    set data [$dataobj blob $cname]
931                    if 0 {
932                        set f [open "/tmp/values-$cname.txt" "w"]
933                        puts $f [$dataobj values $cname]
934                        close $f
935                    }
936                } else {
937                    set data [$dataobj vtkdata $cname]
938                    if 0 {
939                        set f [open "/tmp/volume.vtk" "w"]
940                        puts $f $data
941                        close $f
942                    }
943                }
944                set nbytes [string length $data]
945                if { $_reportClientInfo }  {
946                    set info {}
947                    lappend info "tool_id"       [$dataobj hints toolId]
948                    lappend info "tool_name"     [$dataobj hints toolName]
949                    lappend info "tool_version"  [$dataobj hints toolRevision]
950                    lappend info "tool_title"    [$dataobj hints toolTitle]
951                    lappend info "dataset_label" [$dataobj hints label]
952                    lappend info "dataset_size"  $nbytes
953                    lappend info "dataset_tag"   $tag
954                    SendCmd "clientinfo [list $info]"
955                }
956                SendCmd "volume data follows $nbytes $tag"
957                append _outbuf $data
958                set _recvdDatasets($tag) 1
959                set _serverDatasets($tag) 0
960            }
961            NameTransferFunction $dataobj $cname
962        }
963    }
964    set _first [lindex [get] 0]
965    if { $_reset } {
966        #
967        # Reset the camera and other view parameters
968        #
969        set _settings($this-qw)    $_view(qw)
970        set _settings($this-qx)    $_view(qx)
971        set _settings($this-qy)    $_view(qy)
972        set _settings($this-qz)    $_view(qz)
973        set _settings($this-xpan)  $_view(xpan)
974        set _settings($this-ypan)  $_view(ypan)
975        set _settings($this-zoom)  $_view(zoom)
976
977        set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
978        $_arcball quaternion $q
979        SendCmd "camera orient $q"
980        SendCmd "camera reset"
981        PanCamera
982        SendCmd "camera zoom $_view(zoom)"
983
984        foreach axis {x y z} {
985            # Turn off cutplanes for all volumes
986            SendCmd "cutplane state 0 $axis"
987        }
988
989        InitSettings light2side ambient diffuse specularLevel specularExponent \
990            transp isosurface grid axes \
991            xcutplane ycutplane zcutplane current
992
993        if {"" != $_first} {
994            set axis [$_first hints updir]
995            if { "" != $axis } {
996                SendCmd "up $axis"
997            }
998            set location [$_first hints camera]
999            if { $location != "" } {
1000                array set _view $location
1001            }
1002        }
1003    }
1004    # Outline seems to need to be reset every update.
1005    InitSettings outline cutplaneVisible
1006    # nothing to send -- activate the proper ivol
1007    SendCmd "volume state 0"
1008    if {"" != $_first} {
1009        set datasets [array names _serverDatasets $_first-*]
1010        if { $datasets != "" } {
1011            SendCmd "volume state 1 $datasets"
1012        }
1013        # If the first volume already exists on the server, then make sure
1014        # we display the proper transfer function in the legend.
1015        set cname [lindex [$_first components] 0]
1016        if { [info exists _serverDatasets($_first-$cname)] } {
1017            updateTransferFunctions
1018        }
1019    }
1020    # Actually write the commands to the server socket.  If it fails, we don't
1021    # care.  We're finished here.
1022    blt::busy hold $itk_component(hull)
1023    StopBufferingCommands
1024    blt::busy release $itk_component(hull)
1025    set _reset 0
1026}
1027
1028# ----------------------------------------------------------------------
1029# USAGE: CurrentDatasets ?-cutplanes?
1030#
1031# Returns a list of volume server IDs for the current volume being
1032# displayed.  This is normally a single ID, but it might be a list
1033# of IDs if the current data object has multiple components.
1034# ----------------------------------------------------------------------
1035itcl::body Rappture::NanovisViewer::CurrentDatasets {{what -all}} {
1036    set rlist ""
1037    if { $_first == "" } {
1038        return
1039    }
1040    foreach cname [$_first components] {
1041        set tag $_first-$cname
1042        if { [info exists _serverDatasets($tag)] && $_serverDatasets($tag) } {
1043            array set style {
1044                -cutplanes 1
1045            }
1046            array set style [lindex [$_first components -style $cname] 0]
1047            if { $what != "-cutplanes" || $style(-cutplanes) } {
1048                lappend rlist $tag
1049            }
1050        }
1051    }
1052    return $rlist
1053}
1054
1055# ----------------------------------------------------------------------
1056# USAGE: Zoom in
1057# USAGE: Zoom out
1058# USAGE: Zoom reset
1059#
1060# Called automatically when the user clicks on one of the zoom
1061# controls for this widget.  Changes the zoom for the current view.
1062# ----------------------------------------------------------------------
1063itcl::body Rappture::NanovisViewer::Zoom {option} {
1064    switch -- $option {
1065        "in" {
1066            set _view(zoom) [expr {$_view(zoom)*1.25}]
1067            set _settings($this-zoom) $_view(zoom)
1068            SendCmd "camera zoom $_view(zoom)"
1069        }
1070        "out" {
1071            set _view(zoom) [expr {$_view(zoom)*0.8}]
1072            set _settings($this-zoom) $_view(zoom)
1073            SendCmd "camera zoom $_view(zoom)"
1074        }
1075        "reset" {
1076            array set _view {
1077                qw      0.853553
1078                qx      -0.353553
1079                qy      0.353553
1080                qz      0.146447
1081                zoom    1.0
1082                xpan   0
1083                ypan   0
1084            }
1085            if { $_first != "" } {
1086                set location [$_first hints camera]
1087                if { $location != "" } {
1088                    array set _view $location
1089                }
1090            }
1091            set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
1092            $_arcball quaternion $q
1093            SendCmd "camera orient $q"
1094            SendCmd "camera reset"
1095            set _settings($this-qw)    $_view(qw)
1096            set _settings($this-qx)    $_view(qx)
1097            set _settings($this-qy)    $_view(qy)
1098            set _settings($this-qz)    $_view(qz)
1099            set _settings($this-xpan)  $_view(xpan)
1100            set _settings($this-ypan)  $_view(ypan)
1101            set _settings($this-zoom)  $_view(zoom)
1102        }
1103    }
1104}
1105
1106itcl::body Rappture::NanovisViewer::PanCamera {} {
1107    set x $_view(xpan)
1108    set y $_view(ypan)
1109    SendCmd "camera pan $x $y"
1110}
1111
1112
1113# ----------------------------------------------------------------------
1114# USAGE: Rotate click <x> <y>
1115# USAGE: Rotate drag <x> <y>
1116# USAGE: Rotate release <x> <y>
1117#
1118# Called automatically when the user clicks/drags/releases in the
1119# plot area.  Moves the plot according to the user's actions.
1120# ----------------------------------------------------------------------
1121itcl::body Rappture::NanovisViewer::Rotate {option x y} {
1122    switch -- $option {
1123        click {
1124            $itk_component(3dview) configure -cursor fleur
1125            set _click(x) $x
1126            set _click(y) $y
1127        }
1128        drag {
1129            if {[array size _click] == 0} {
1130                Rotate click $x $y
1131            } else {
1132                set w [winfo width $itk_component(3dview)]
1133                set h [winfo height $itk_component(3dview)]
1134                if {$w <= 0 || $h <= 0} {
1135                    return
1136                }
1137
1138                if {[catch {
1139                    # this fails sometimes for no apparent reason
1140                    set dx [expr {double($x-$_click(x))/$w}]
1141                    set dy [expr {double($y-$_click(y))/$h}]
1142                }]} {
1143                    return
1144                }
1145
1146                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1147                foreach { _view(qw) _view(qx) _view(qy) _view(qz) } $q break
1148                set _settings($this-qw) $_view(qw)
1149                set _settings($this-qx) $_view(qx)
1150                set _settings($this-qy) $_view(qy)
1151                set _settings($this-qz) $_view(qz)
1152                SendCmd "camera orient $q"
1153
1154                set _click(x) $x
1155                set _click(y) $y
1156            }
1157        }
1158        release {
1159            Rotate drag $x $y
1160            $itk_component(3dview) configure -cursor ""
1161            catch {unset _click}
1162        }
1163        default {
1164            error "bad option \"$option\": should be click, drag, release"
1165        }
1166    }
1167}
1168
1169# ----------------------------------------------------------------------
1170# USAGE: $this Pan click x y
1171#        $this Pan drag x y
1172#        $this Pan release x y
1173#
1174# Called automatically when the user clicks on one of the zoom
1175# controls for this widget.  Changes the zoom for the current view.
1176# ----------------------------------------------------------------------
1177itcl::body Rappture::NanovisViewer::Pan {option x y} {
1178    # Experimental stuff
1179    set w [winfo width $itk_component(3dview)]
1180    set h [winfo height $itk_component(3dview)]
1181    if { $option == "set" } {
1182        set x [expr $x / double($w)]
1183        set y [expr $y / double($h)]
1184        set _view(xpan) [expr $_view(xpan) + $x]
1185        set _view(ypan) [expr $_view(ypan) + $y]
1186        PanCamera
1187        set _settings($this-xpan) $_view(xpan)
1188        set _settings($this-ypan) $_view(ypan)
1189        return
1190    }
1191    if { $option == "click" } {
1192        set _click(x) $x
1193        set _click(y) $y
1194        $itk_component(3dview) configure -cursor hand1
1195    }
1196    if { $option == "drag" || $option == "release" } {
1197        set dx [expr ($_click(x) - $x)/double($w)]
1198        set dy [expr ($_click(y) - $y)/double($h)]
1199        set _click(x) $x
1200        set _click(y) $y
1201        set _view(xpan) [expr $_view(xpan) - $dx]
1202        set _view(ypan) [expr $_view(ypan) - $dy]
1203        PanCamera
1204        set _settings($this-xpan) $_view(xpan)
1205        set _settings($this-ypan) $_view(ypan)
1206    }
1207    if { $option == "release" } {
1208        $itk_component(3dview) configure -cursor ""
1209    }
1210}
1211
1212# ----------------------------------------------------------------------
1213# USAGE: InitSettings <what> ?<value>?
1214#
1215# Used internally to update rendering settings whenever parameters
1216# change in the popup settings panel.  Sends the new settings off
1217# to the back end.
1218# ----------------------------------------------------------------------
1219itcl::body Rappture::NanovisViewer::InitSettings { args } {
1220    foreach arg $args {
1221        AdjustSetting $arg
1222    }
1223}
1224
1225# ----------------------------------------------------------------------
1226# USAGE: AdjustSetting <what> ?<value>?
1227#
1228# Used internally to update rendering settings whenever parameters
1229# change in the popup settings panel.  Sends the new settings off
1230# to the back end.
1231# ----------------------------------------------------------------------
1232itcl::body Rappture::NanovisViewer::AdjustSetting {what {value ""}} {
1233    if {![isconnected]} {
1234        return
1235    }
1236    switch -- $what {
1237        "current" {
1238            set cname [$itk_component(volcomponents) value]
1239            SwitchComponent $cname
1240        }
1241        ambient {
1242            set val $_settings($this-ambient)
1243            set val [expr {0.01*$val}]
1244            foreach tag [GetDatasetsWithComponent $_current] {
1245                SendCmd "volume shading ambient $val $tag"
1246            }
1247        }
1248        diffuse {
1249            set val $_settings($this-diffuse)
1250            set val [expr {0.01*$val}]
1251            foreach tag [GetDatasetsWithComponent $_current] {
1252                SendCmd "volume shading diffuse $val $tag"
1253            }
1254        }
1255        specularLevel {
1256            set val $_settings($this-specularLevel)
1257            set val [expr {0.01*$val}]
1258            foreach tag [GetDatasetsWithComponent $_current] {
1259                SendCmd "volume shading specularLevel $val $tag"
1260            }
1261        }
1262        specularExponent {
1263            set val $_settings($this-specularExponent)
1264            foreach tag [GetDatasetsWithComponent $_current] {
1265                SendCmd "volume shading specularExp $val $tag"
1266            }
1267        }
1268        light2side {
1269            set _settings($_current-light2side) $_settings($this-light2side)
1270            set val $_settings($this-light2side)
1271            foreach tag [GetDatasetsWithComponent $_current] {
1272                SendCmd "volume shading light2side $val $tag"
1273            }
1274        }
1275        transp {
1276            set _settings($_current-transp) $_settings($this-transp)
1277            set val $_settings($this-transp)
1278            set sval [expr { 0.01 * double($val) }]
1279            foreach tag [GetDatasetsWithComponent $_current] {
1280                SendCmd "volume shading opacity $sval $tag"
1281            }
1282        }
1283        opacity {
1284            error "this should not be called"
1285            set _settings($_current-opacity) $_settings($this-opacity)
1286            updateTransferFunctions
1287        }
1288        thickness {
1289            set val $_settings($this-thickness)
1290            set _settings($_current-thickness) $val
1291            updateTransferFunctions
1292        }
1293        "outline" {
1294            SendCmd "volume outline state $_settings($this-outline)"
1295        }
1296        "isosurface" {
1297            SendCmd "volume shading isosurface $_settings($this-isosurface)"
1298        }
1299        "colormap" {
1300            set color [$itk_component(colormap) value]
1301            set _settings($this-colormap) $color
1302            set _settings($_current-colormap) $color
1303            ResetColormap $_current $color
1304        }
1305        "grid" {
1306            SendCmd "grid visible $_settings($this-grid)"
1307        }
1308        "axes" {
1309            SendCmd "axis visible $_settings($this-axes)"
1310        }
1311        "legend" {
1312            if { $_settings($this-legend) } {
1313                blt::table $itk_component(plotarea) \
1314                    0,0 $itk_component(3dview) -fill both \
1315                    1,0 $itk_component(legend) -fill x
1316                blt::table configure $itk_component(plotarea) r1 -resize none
1317            } else {
1318                blt::table forget $itk_component(legend)
1319            }
1320        }
1321        "volume" {
1322            # This is the global volume visibility control.  It controls the
1323            # visibility of all the all volumes.  Whenever it's changed, you
1324            # have to synchronize each of the local controls (see below) with
1325            # this.
1326            set datasets [CurrentDatasets]
1327            set bool $_settings($this-volume)
1328            SendCmd "volume data state $bool $datasets"
1329            foreach cname $_componentsList {
1330                set _settings($cname-volumeVisible) $bool
1331            }
1332            set _settings($this-volumeVisible) $bool
1333        }
1334        "volumeVisible" {
1335            # This is the component specific control.  It changes the
1336            # visibility of only the current component.
1337            set _settings($_current-volumeVisible) \
1338                $_settings($this-volumeVisible)
1339            foreach tag [GetDatasetsWithComponent $_current] {
1340                SendCmd "volume data state $_settings($this-volumeVisible) $tag"
1341            }
1342        }
1343        "cutplaneVisible" {
1344            set bool $_settings($this-$what)
1345            set datasets [CurrentDatasets -cutplanes]
1346            set tag [lindex $datasets 0]
1347            SendCmd "cutplane visible $bool $tag"
1348        }
1349        "xcutplane" - "ycutplane" - "zcutplane" {
1350            set axis [string range $what 0 0]
1351            set bool $_settings($this-$what)
1352            set datasets [CurrentDatasets -cutplanes]
1353            set tag [lindex $datasets 0]
1354            SendCmd "cutplane state $bool $axis $tag"
1355            if { $bool } {
1356                $itk_component(${axis}CutScale) configure -state normal \
1357                    -troughcolor white
1358            } else {
1359                $itk_component(${axis}CutScale) configure -state disabled \
1360                    -troughcolor grey82
1361            }
1362        }
1363        default {
1364            error "don't know how to fix $what"
1365        }
1366    }
1367}
1368
1369# ----------------------------------------------------------------------
1370# USAGE: FixLegend
1371#
1372# Used internally to update the legend area whenever it changes size
1373# or when the field changes.  Asks the server to send a new legend
1374# for the current field.
1375# ----------------------------------------------------------------------
1376itcl::body Rappture::NanovisViewer::FixLegend {} {
1377    set _resizeLegendPending 0
1378    set lineht [font metrics $itk_option(-font) -linespace]
1379    set w [expr {$_width-20}]
1380    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
1381    if {$w > 0 && $h > 0 && $_first != "" } {
1382        if { [info exists _cname2transferFunction($_current)] } {
1383            SendCmd "legend $_current $w $h"
1384        }
1385    }
1386}
1387
1388#
1389# NameTransferFunction --
1390#
1391#       Creates a transfer function name based on the <style> settings in the
1392#       library run.xml file. This placeholder will be used later to create
1393#       and send the actual transfer function once the data info has been sent
1394#       to us by the render server. [We won't know the volume limits until the
1395#       server parses the 3D data and sends back the limits via ReceiveData.]
1396#
1397itcl::body Rappture::NanovisViewer::NameTransferFunction { dataobj cname } {
1398    array set style {
1399        -color BCGYR
1400        -levels 6
1401        -opacity 1.0
1402        -markers ""
1403    }
1404    set tag $dataobj-$cname
1405    array set style [lindex [$dataobj components -style $cname] 0]
1406    if { ![info exists _cname2transferFunction($cname)] } {
1407        # Get the colormap right now, since it doesn't change with marker
1408        # changes.
1409        set cmap [ColorsToColormap $style(-color)]
1410        set wmap [list 0.0 0.0 1.0 1.0]
1411        set _cname2transferFunction($cname) [list $cmap $wmap]
1412        SendCmd [list transfunc define $cname $cmap $wmap]
1413    }
1414    SendCmd "volume shading transfunc $cname $tag"
1415    if { ![info exists _transferFunctionEditors($cname)] } {
1416        set _transferFunctionEditors($cname) \
1417            [Rappture::TransferFunctionEditor ::\#auto $itk_component(legend) \
1418                 $cname \
1419                 -command [itcl::code $this updateTransferFunctions]]
1420    }
1421    set _dataset2style($tag) $cname
1422    lappend _style2datasets($cname) $tag
1423    return $cname
1424}
1425
1426#
1427# ComputeTransferFunction --
1428#
1429#       Computes and sends the transfer function to the render server.  It's
1430#       assumed that the volume data limits are known and that the global
1431#       transfer-functions slider values have been set up.  Both parts are
1432#       needed to compute the relative value (location) of the marker, and
1433#       the alpha map of the transfer function.
1434#
1435itcl::body Rappture::NanovisViewer::ComputeTransferFunction { cname } {
1436    foreach {cmap wmap} $_cname2transferFunction($cname) break
1437
1438    # We have to parse the style attributes for a volume using this
1439    # transfer-function *once*.  This sets up the initial isomarkers for the
1440    # transfer function.  The user may add/delete markers, so we have to
1441    # maintain a list of markers for each transfer-function.  We use the one
1442    # of the volumes (the first in the list) using the transfer-function as a
1443    # reference.
1444    if { ![info exists _parsedFunction($cname)] } {
1445        array set style {
1446            -color BCGYR
1447            -levels 6
1448            -opacity 1.0
1449            -markers ""
1450        }
1451        # Accumulate the style from all the datasets using it.
1452        foreach tag [GetDatasetsWithComponent $cname] {
1453            foreach {dataobj cname} [split [lindex $tag 0] -] break
1454            array set style [lindex [$dataobj components -style $cname] 0]
1455        }
1456        eval $_transferFunctionEditors($cname) limits $_limits($cname)
1457        # Have to defer creation of isomarkers until we have data limits
1458        if { [info exists style(-markers)] &&
1459             [llength $style(-markers)] > 0 } {
1460            ParseMarkersOption $cname $style(-markers)
1461        } else {
1462            ParseLevelsOption $cname $style(-levels)
1463        }
1464       
1465    }
1466    set wmap [ComputeAlphamap $cname]
1467    set _cname2transferFunction($cname) [list $cmap $wmap]
1468    SendCmd [list transfunc define $cname $cmap $wmap]
1469}
1470
1471# ----------------------------------------------------------------------
1472# CONFIGURATION OPTION: -plotbackground
1473# ----------------------------------------------------------------------
1474itcl::configbody Rappture::NanovisViewer::plotbackground {
1475    if { [isconnected] } {
1476        foreach {r g b} [Color2RGB $itk_option(-plotbackground)] break
1477        #fix this!
1478        #SendCmd "color background $r $g $b"
1479    }
1480}
1481
1482# ----------------------------------------------------------------------
1483# CONFIGURATION OPTION: -plotforeground
1484# ----------------------------------------------------------------------
1485itcl::configbody Rappture::NanovisViewer::plotforeground {
1486    if { [isconnected] } {
1487        foreach {r g b} [Color2RGB $itk_option(-plotforeground)] break
1488        #fix this!
1489        #SendCmd "color background $r $g $b"
1490    }
1491}
1492
1493# ----------------------------------------------------------------------
1494# CONFIGURATION OPTION: -plotoutline
1495# ----------------------------------------------------------------------
1496itcl::configbody Rappture::NanovisViewer::plotoutline {
1497    # Must check if we are connected because this routine is called from the
1498    # class body when the -plotoutline itk_option is defined.  At that point
1499    # the NanovisViewer class constructor hasn't been called, so we can't
1500    # start sending commands to visualization server.
1501    if { [isconnected] } {
1502        if {"" == $itk_option(-plotoutline)} {
1503            SendCmd "volume outline state off"
1504        } else {
1505            SendCmd "volume outline state on"
1506            SendCmd "volume outline color [Color2RGB $itk_option(-plotoutline)]"
1507        }
1508    }
1509}
1510
1511#
1512# The -levels option takes a single value that represents the number
1513# of evenly distributed markers based on the current data range. Each
1514# marker is a relative value from 0.0 to 1.0.
1515#
1516itcl::body Rappture::NanovisViewer::ParseLevelsOption { cname levels } {
1517    set c $itk_component(legend)
1518    set list {}
1519    regsub -all "," $levels " " levels
1520    if {[string is int $levels]} {
1521        for {set i 1} { $i <= $levels } {incr i} {
1522            lappend list [expr {double($i)/($levels+1)}]
1523        }
1524    } else {
1525        foreach x $levels {
1526            lappend list $x
1527        }
1528    }
1529    set _parsedFunction($cname) 1
1530    $_transferFunctionEditors($cname) addMarkers $list
1531}
1532
1533#
1534# The -markers option takes a list of zero or more values (the values
1535# may be separated either by spaces or commas) that have the following
1536# format:
1537#
1538#   N%  Percent of current total data range.  Converted to
1539#       to a relative value between 0.0 and 1.0.
1540#   N   Absolute value of marker.  If the marker is outside of
1541#       the current range, it will be displayed on the outer
1542#       edge of the legends, but it range it represents will
1543#       not be seen.
1544#
1545itcl::body Rappture::NanovisViewer::ParseMarkersOption { cname markers } {
1546    set c $itk_component(legend)
1547    set list {}
1548    foreach { min max } $_limits($cname) break
1549    regsub -all "," $markers " " markers
1550    foreach marker $markers {
1551        set n [scan $marker "%g%s" value suffix]
1552        if { $n == 2 && $suffix == "%" } {
1553            # $n% : Set relative value (0..1).
1554            lappend list [expr {$value * 0.01}]
1555        } else {
1556            # $n : absolute value, compute relative
1557            lappend list  [expr {(double($value)-$min)/($max-$min)]}
1558        }
1559    }
1560    set _parsedFunction($cname) 1
1561    $_transferFunctionEditors($cname) addMarkers $list
1562}
1563
1564# ----------------------------------------------------------------------
1565# USAGE: UndateTransferFuncs
1566# ----------------------------------------------------------------------
1567itcl::body Rappture::NanovisViewer::updateTransferFunctions {} {
1568    $_dispatcher event -idle !send_transfunc
1569}
1570
1571itcl::body Rappture::NanovisViewer::limits { cname } {
1572    set _limits(min) 0.0
1573    set _limits(max) 1.0
1574    if { ![info exists _style2datasets($cname)] } {
1575        return [array get _limits]
1576    }
1577    set min ""; set max ""
1578    foreach tag [GetDatasetsWithComponent $cname] {
1579        if { ![info exists _limits($tag)] } {
1580            continue
1581        }
1582        foreach {amin amax} $_limits($tag) break
1583        if { $min == "" || $min > $amin } {
1584            set min $amin
1585        }
1586        if { $max == "" || $max < $amax } {
1587            set max $amax
1588        }
1589    }
1590    if { $min != "" } {
1591        set _limits(min) $min
1592    }
1593    if { $max != "" } {
1594        set _limits(max) $max
1595    }
1596    return [list $_limits(min) $_limits(max)]
1597}
1598
1599
1600itcl::body Rappture::NanovisViewer::BuildViewTab {} {
1601    foreach { key value } {
1602        grid            0
1603        axes            1
1604        outline         0
1605        volume          1
1606        legend          1
1607        particles       1
1608        lic             1
1609    } {
1610        set _settings($this-$key) $value
1611    }
1612
1613    set fg [option get $itk_component(hull) font Font]
1614    #set bfg [option get $itk_component(hull) boldFont Font]
1615
1616    set inner [$itk_component(main) insert end \
1617        -title "View Settings" \
1618        -icon [Rappture::icon wrench]]
1619    $inner configure -borderwidth 4
1620
1621    set ::Rappture::NanovisViewer::_settings($this-isosurface) 0
1622    checkbutton $inner.isosurface \
1623        -text "Isosurface shading" \
1624        -variable [itcl::scope _settings($this-isosurface)] \
1625        -command [itcl::code $this AdjustSetting isosurface] \
1626        -font "Arial 9"
1627
1628    checkbutton $inner.axes \
1629        -text "Axes" \
1630        -variable [itcl::scope _settings($this-axes)] \
1631        -command [itcl::code $this AdjustSetting axes] \
1632        -font "Arial 9"
1633
1634    checkbutton $inner.grid \
1635        -text "Grid" \
1636        -variable [itcl::scope _settings($this-grid)] \
1637        -command [itcl::code $this AdjustSetting grid] \
1638        -font "Arial 9"
1639
1640    checkbutton $inner.outline \
1641        -text "Outline" \
1642        -variable [itcl::scope _settings($this-outline)] \
1643        -command [itcl::code $this AdjustSetting outline] \
1644        -font "Arial 9"
1645
1646    checkbutton $inner.legend \
1647        -text "Legend" \
1648        -variable [itcl::scope _settings($this-legend)] \
1649        -command [itcl::code $this AdjustSetting legend] \
1650        -font "Arial 9"
1651
1652    checkbutton $inner.volume \
1653        -text "Volume" \
1654        -variable [itcl::scope _settings($this-volume)] \
1655        -command [itcl::code $this AdjustSetting volume] \
1656        -font "Arial 9"
1657
1658    blt::table $inner \
1659        0,0 $inner.axes  -cspan 2 -anchor w \
1660        1,0 $inner.grid  -cspan 2 -anchor w \
1661        2,0 $inner.outline  -cspan 2 -anchor w \
1662        3,0 $inner.volume  -cspan 2 -anchor w \
1663        4,0 $inner.legend  -cspan 2 -anchor w
1664
1665    if 0 {
1666    bind $inner <Map> [itcl::code $this GetVolumeInfo $inner]
1667    }
1668    blt::table configure $inner r* -resize none
1669    blt::table configure $inner r5 -resize expand
1670}
1671
1672itcl::body Rappture::NanovisViewer::BuildVolumeTab {} {
1673    set inner [$itk_component(main) insert end \
1674        -title "Volume Settings" \
1675        -icon [Rappture::icon volume-on]]
1676    $inner configure -borderwidth 4
1677
1678    set font [option get $itk_component(hull) font Font]
1679    #set bfont [option get $itk_component(hull) boldFont Font]
1680
1681    label $inner.lighting_l \
1682        -text "Lighting / Material Properties" \
1683        -font "Arial 9 bold"
1684
1685    checkbutton $inner.light2side \
1686        -text "Two-sided lighting" \
1687        -font $font \
1688        -variable [itcl::scope _settings($this-light2side)] \
1689        -command [itcl::code $this AdjustSetting light2side]
1690
1691    checkbutton $inner.visibility \
1692        -text "Visible" \
1693        -font $font \
1694        -variable [itcl::scope _settings($this-volumeVisible)] \
1695        -command [itcl::code $this AdjustSetting volumeVisible] \
1696
1697    label $inner.ambient_l \
1698        -text "Ambient" \
1699        -font $font
1700    ::scale $inner.ambient -from 0 -to 100 -orient horizontal \
1701        -variable [itcl::scope _settings($this-ambient)] \
1702        -showvalue off -command [itcl::code $this AdjustSetting ambient] \
1703        -troughcolor grey92
1704
1705    label $inner.diffuse_l -text "Diffuse" -font $font
1706    ::scale $inner.diffuse -from 0 -to 100 -orient horizontal \
1707        -variable [itcl::scope _settings($this-diffuse)] \
1708        -showvalue off -command [itcl::code $this AdjustSetting diffuse] \
1709        -troughcolor grey92
1710
1711    label $inner.specularLevel_l -text "Specular" -font $font
1712    ::scale $inner.specularLevel -from 0 -to 100 -orient horizontal \
1713        -variable [itcl::scope _settings($this-specularLevel)] \
1714        -showvalue off -command [itcl::code $this AdjustSetting specularLevel] \
1715        -troughcolor grey92
1716
1717    label $inner.specularExponent_l -text "Shininess" -font $font
1718    ::scale $inner.specularExponent -from 10 -to 128 -orient horizontal \
1719        -variable [itcl::scope _settings($this-specularExponent)] \
1720        -showvalue off \
1721        -command [itcl::code $this AdjustSetting specularExponent] \
1722        -troughcolor grey92
1723
1724    label $inner.transp_l -text "Opacity" -font $font
1725    ::scale $inner.transp -from 0 -to 100 -orient horizontal \
1726        -variable [itcl::scope _settings($this-transp)] \
1727        -showvalue off -command [itcl::code $this AdjustSetting transp] \
1728        -troughcolor grey92
1729
1730    label $inner.transferfunction_l \
1731        -text "Transfer Function" -font "Arial 9 bold"
1732
1733    label $inner.thin -text "Thin" -font $font
1734    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
1735        -variable [itcl::scope _settings($this-thickness)] \
1736        -showvalue off -command [itcl::code $this AdjustSetting thickness] \
1737        -troughcolor grey92
1738
1739    label $inner.thick -text "Thick" -font $font
1740
1741    label $inner.colormap_l -text "Colormap" -font $font
1742    itk_component add colormap {
1743        Rappture::Combobox $inner.colormap -width 10 -editable no
1744    }
1745
1746    $inner.colormap choices insert end \
1747        "default"            "default"            \
1748        "BCGYR"              "BCGYR"            \
1749        "BGYOR"              "BGYOR"            \
1750        "blue"               "blue"             \
1751        "blue-to-brown"      "blue-to-brown"    \
1752        "blue-to-orange"     "blue-to-orange"   \
1753        "blue-to-grey"       "blue-to-grey"     \
1754        "green-to-magenta"   "green-to-magenta" \
1755        "greyscale"          "greyscale"        \
1756        "nanohub"            "nanohub"          \
1757        "rainbow"            "rainbow"          \
1758        "spectral"           "spectral"         \
1759        "ROYGB"              "ROYGB"            \
1760        "RYGCB"              "RYGCB"            \
1761        "brown-to-blue"      "brown-to-blue"    \
1762        "grey-to-blue"       "grey-to-blue"     \
1763        "orange-to-blue"     "orange-to-blue"   \
1764        "none"               "none"
1765
1766    bind $inner.colormap <<Value>> \
1767        [itcl::code $this AdjustSetting colormap]
1768    $itk_component(colormap) value "default"
1769    set _settings($this-colormap) "default"
1770
1771    label $inner.volcomponents_l -text "Component" -font $font
1772    itk_component add volcomponents {
1773        Rappture::Combobox $inner.volcomponents -editable no
1774    }
1775    $itk_component(volcomponents) value "BCGYR"
1776    bind $inner.volcomponents <<Value>> \
1777        [itcl::code $this AdjustSetting current]
1778
1779    blt::table $inner \
1780        0,0 $inner.volcomponents_l -anchor e -cspan 2 \
1781        0,2 $inner.volcomponents             -cspan 3 -fill x \
1782        1,1 $inner.lighting_l -anchor w -cspan 4 \
1783        2,1 $inner.ambient_l       -anchor e -pady 2 \
1784        2,2 $inner.ambient                   -cspan 3 -fill x \
1785        3,1 $inner.diffuse_l       -anchor e -pady 2 \
1786        3,2 $inner.diffuse                   -cspan 3 -fill x \
1787        4,1 $inner.specularLevel_l -anchor e -pady 2 \
1788        4,2 $inner.specularLevel             -cspan 3 -fill x \
1789        5,1 $inner.specularExponent_l -anchor e -pady 2 \
1790        5,2 $inner.specularExponent          -cspan 3 -fill x \
1791        6,1 $inner.light2side -cspan 3 -anchor w \
1792        7,1 $inner.visibility -cspan 3 -anchor w \
1793        8,1 $inner.transferfunction_l -anchor w              -cspan 4 \
1794        9,1 $inner.transp_l -anchor e -pady 2 \
1795        9,2 $inner.transp                    -cspan 3 -fill x \
1796        10,1 $inner.colormap_l       -anchor e \
1797        10,2 $inner.colormap                  -padx 2 -cspan 3 -fill x \
1798        11,1 $inner.thin             -anchor e \
1799        11,2 $inner.thickness                 -cspan 2 -fill x \
1800        11,4 $inner.thick -anchor w 
1801
1802    blt::table configure $inner c* r* -resize none
1803    blt::table configure $inner r* -pady { 2 0 }
1804    blt::table configure $inner c2 c3 r12 -resize expand
1805    blt::table configure $inner c0 -width .1i
1806}
1807
1808itcl::body Rappture::NanovisViewer::BuildCutplanesTab {} {
1809    set inner [$itk_component(main) insert end \
1810        -title "Cutplane Settings" \
1811        -icon [Rappture::icon cutbutton]]
1812    $inner configure -borderwidth 4
1813
1814    checkbutton $inner.visible \
1815        -text "Show Cutplanes" \
1816        -variable [itcl::scope _settings($this-cutplaneVisible)] \
1817        -command [itcl::code $this AdjustSetting cutplaneVisible] \
1818        -font "Arial 9"
1819
1820    # X-value slicer...
1821    itk_component add xCutButton {
1822        Rappture::PushButton $inner.xbutton \
1823            -onimage [Rappture::icon x-cutplane] \
1824            -offimage [Rappture::icon x-cutplane] \
1825            -command [itcl::code $this AdjustSetting xcutplane] \
1826            -variable [itcl::scope _settings($this-xcutplane)]
1827    }
1828    Rappture::Tooltip::for $itk_component(xCutButton) \
1829        "Toggle the X cut plane on/off"
1830    $itk_component(xCutButton) select
1831
1832    itk_component add xCutScale {
1833        ::scale $inner.xval -from 100 -to 0 \
1834            -width 10 -orient vertical -showvalue off \
1835            -borderwidth 1 -highlightthickness 0 \
1836            -command [itcl::code $this Slice move x] \
1837            -variable [itcl::scope _settings($this-xcutposition)]
1838    } {
1839        usual
1840        ignore -borderwidth -highlightthickness
1841    }
1842    # Set the default cutplane value before disabling the scale.
1843    $itk_component(xCutScale) set 50
1844    $itk_component(xCutScale) configure -state disabled
1845    Rappture::Tooltip::for $itk_component(xCutScale) \
1846        "@[itcl::code $this SlicerTip x]"
1847
1848    # Y-value slicer...
1849    itk_component add yCutButton {
1850        Rappture::PushButton $inner.ybutton \
1851            -onimage [Rappture::icon y-cutplane] \
1852            -offimage [Rappture::icon y-cutplane] \
1853            -command [itcl::code $this AdjustSetting ycutplane] \
1854            -variable [itcl::scope _settings($this-ycutplane)]
1855    }
1856    Rappture::Tooltip::for $itk_component(yCutButton) \
1857        "Toggle the Y cut plane on/off"
1858    $itk_component(yCutButton) select
1859
1860    itk_component add yCutScale {
1861        ::scale $inner.yval -from 100 -to 0 \
1862            -width 10 -orient vertical -showvalue off \
1863            -borderwidth 1 -highlightthickness 0 \
1864            -command [itcl::code $this Slice move y] \
1865            -variable [itcl::scope _settings($this-ycutposition)]
1866    } {
1867        usual
1868        ignore -borderwidth -highlightthickness
1869    }
1870    Rappture::Tooltip::for $itk_component(yCutScale) \
1871        "@[itcl::code $this SlicerTip y]"
1872    # Set the default cutplane value before disabling the scale.
1873    $itk_component(yCutScale) set 50
1874    $itk_component(yCutScale) configure -state disabled
1875
1876    # Z-value slicer...
1877    itk_component add zCutButton {
1878        Rappture::PushButton $inner.zbutton \
1879            -onimage [Rappture::icon z-cutplane] \
1880            -offimage [Rappture::icon z-cutplane] \
1881            -command [itcl::code $this AdjustSetting zcutplane] \
1882            -variable [itcl::scope _settings($this-zcutplane)]
1883    }
1884    Rappture::Tooltip::for $itk_component(zCutButton) \
1885        "Toggle the Z cut plane on/off"
1886    $itk_component(zCutButton) select
1887
1888    itk_component add zCutScale {
1889        ::scale $inner.zval -from 100 -to 0 \
1890            -width 10 -orient vertical -showvalue off \
1891            -borderwidth 1 -highlightthickness 0 \
1892            -command [itcl::code $this Slice move z] \
1893            -variable [itcl::scope _settings($this-zcutposition)]
1894    } {
1895        usual
1896        ignore -borderwidth -highlightthickness
1897    }
1898    $itk_component(zCutScale) set 50
1899    $itk_component(zCutScale) configure -state disabled
1900    Rappture::Tooltip::for $itk_component(zCutScale) \
1901        "@[itcl::code $this SlicerTip z]"
1902
1903    blt::table $inner \
1904        0,1 $inner.visible              -anchor w -pady 2 -cspan 4 \
1905        1,1 $itk_component(xCutScale) \
1906        1,2 $itk_component(yCutScale) \
1907        1,3 $itk_component(zCutScale) \
1908        2,1 $itk_component(xCutButton) \
1909        2,2 $itk_component(yCutButton) \
1910        2,3 $itk_component(zCutButton)
1911
1912    blt::table configure $inner r0 r1 r2 c* -resize none
1913    blt::table configure $inner r3 c4 -resize expand
1914    blt::table configure $inner c0 -width 2
1915    blt::table configure $inner c1 c2 c3 -padx 2
1916}
1917
1918itcl::body Rappture::NanovisViewer::BuildCameraTab {} {
1919    set inner [$itk_component(main) insert end \
1920        -title "Camera Settings" \
1921        -icon [Rappture::icon camera]]
1922    $inner configure -borderwidth 4
1923
1924    label $inner.view_l -text "view" -font "Arial 9"
1925    set f [frame $inner.view]
1926    foreach side { front back left right top bottom } {
1927        button $f.$side  -image [Rappture::icon view$side] \
1928            -command [itcl::code $this SetOrientation $side]
1929        Rappture::Tooltip::for $f.$side "Change the view to $side"
1930        pack $f.$side -side left
1931    }
1932
1933    blt::table $inner \
1934        0,0 $inner.view_l -anchor e -pady 2 \
1935        0,1 $inner.view -anchor w -pady 2
1936
1937    set row 1
1938    set labels { qw qx qy qz xpan ypan zoom }
1939    foreach tag $labels {
1940        label $inner.${tag}label -text $tag -font "Arial 9"
1941        entry $inner.${tag} -font "Arial 9"  -bg white \
1942            -textvariable [itcl::scope _settings($this-$tag)]
1943        bind $inner.${tag} <Return> \
1944            [itcl::code $this camera set ${tag}]
1945        bind $inner.${tag} <KP_Enter> \
1946            [itcl::code $this camera set ${tag}]
1947        blt::table $inner \
1948            $row,0 $inner.${tag}label -anchor e -pady 2 \
1949            $row,1 $inner.${tag} -anchor w -pady 2
1950        blt::table configure $inner r$row -resize none
1951        incr row
1952    }
1953
1954    blt::table configure $inner c* r* -resize none
1955    blt::table configure $inner c2 -resize expand
1956    blt::table configure $inner r$row -resize expand
1957}
1958
1959# ----------------------------------------------------------------------
1960# USAGE: Slice move x|y|z <newval>
1961#
1962# Called automatically when the user drags the slider to move the
1963# cut plane that slices 3D data.  Gets the current value from the
1964# slider and moves the cut plane to the appropriate point in the
1965# data set.
1966# ----------------------------------------------------------------------
1967itcl::body Rappture::NanovisViewer::Slice {option args} {
1968    switch -- $option {
1969        move {
1970            if {[llength $args] != 2} {
1971                error "wrong # args: should be \"Slice move x|y|z newval\""
1972            }
1973            set axis [lindex $args 0]
1974            set newval [lindex $args 1]
1975
1976            set newpos [expr {0.01*$newval}]
1977            set datasets [CurrentDatasets -cutplanes]
1978            set tag [lindex $datasets 0]
1979            SendCmd "cutplane position $newpos $axis $tag"
1980        }
1981        default {
1982            error "bad option \"$option\": should be axis, move, or volume"
1983        }
1984    }
1985}
1986
1987# ----------------------------------------------------------------------
1988# USAGE: SlicerTip <axis>
1989#
1990# Used internally to generate a tooltip for the x/y/z slicer controls.
1991# Returns a message that includes the current slicer value.
1992# ----------------------------------------------------------------------
1993itcl::body Rappture::NanovisViewer::SlicerTip {axis} {
1994    set val [$itk_component(${axis}CutScale) get]
1995    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
1996}
1997
1998
1999itcl::body Rappture::NanovisViewer::DoResize {} {
2000    $_arcball resize $_width $_height
2001    SendCmd "screen size $_width $_height"
2002    set _resizePending 0
2003}
2004
2005itcl::body Rappture::NanovisViewer::EventuallyResize { w h } {
2006    set _width $w
2007    set _height $h
2008    $_arcball resize $w $h
2009    if { !$_resizePending } {
2010        $_dispatcher event -idle !resize
2011        set _resizePending 1
2012    }
2013}
2014
2015itcl::body Rappture::NanovisViewer::EventuallyRedrawLegend {} {
2016    if { !$_resizeLegendPending } {
2017        $_dispatcher event -idle !legend
2018        set _resizeLegendPending 1
2019    }
2020}
2021
2022#  camera --
2023#
2024itcl::body Rappture::NanovisViewer::camera {option args} {
2025    switch -- $option {
2026        "show" {
2027            puts [array get _view]
2028        }
2029        "set" {
2030            set who [lindex $args 0]
2031            set x $_settings($this-$who)
2032            set code [catch { string is double $x } result]
2033            if { $code != 0 || !$result } {
2034                set _settings($this-$who) $_view($who)
2035                return
2036            }
2037            switch -- $who {
2038                "xpan" - "ypan" {
2039                    set _view($who) $_settings($this-$who)
2040                    PanCamera
2041                }
2042                "qx" - "qy" - "qz" - "qw" {
2043                    set _view($who) $_settings($this-$who)
2044                    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
2045                    $_arcball quaternion $q
2046                    SendCmd "camera orient $q"
2047                }
2048                "zoom" {
2049                    set _view($who) $_settings($this-$who)
2050                    SendCmd "camera zoom $_view(zoom)"
2051                }
2052            }
2053        }
2054    }
2055}
2056
2057itcl::body Rappture::NanovisViewer::GetVolumeInfo { w } {
2058    set flowobj ""
2059    foreach key [array names _obj2flow] {
2060        set flowobj $_obj2flow($key)
2061        break
2062    }
2063    if { $flowobj == "" } {
2064        return
2065    }
2066    if { [winfo exists $w.frame] } {
2067        destroy $w.frame
2068    }
2069    set inner [frame $w.frame]
2070    blt::table $w \
2071        5,0 $inner -fill both -cspan 2 -anchor nw
2072    array set hints [$dataobj hints]
2073
2074    label $inner.volumes -text "Volumes" -font "Arial 9 bold"
2075    blt::table $inner \
2076        1,0 $inner.volumes  -anchor w \
2077    blt::table configure $inner c0 c1 -resize none
2078    blt::table configure $inner c2 -resize expand
2079
2080    set row 3
2081    set volumes [get]
2082    if { [llength $volumes] > 0 } {
2083        blt::table $inner $row,0 $inner.volumes  -anchor w
2084        incr row
2085    }
2086    foreach vol $volumes {
2087        array unset info
2088        array set info $vol
2089        set name $info(name)
2090        if { ![info exists _settings($this-volume-$name)] } {
2091            set _settings($this-volume-$name) $info(hide)
2092        }
2093        checkbutton $inner.vol$row -text $info(label) \
2094            -variable [itcl::scope _settings($this-volume-$name)] \
2095            -onvalue 0 -offvalue 1 \
2096            -command [itcl::code $this ToggleVolume $key $name] \
2097            -font "Arial 9"
2098        Rappture::Tooltip::for $inner.vol$row $info(description)
2099        blt::table $inner $row,0 $inner.vol$row -anchor w
2100        if { !$_settings($this-volume-$name) } {
2101            $inner.vol$row select
2102        }
2103        incr row
2104    }
2105    blt::table configure $inner r* -resize none
2106    blt::table configure $inner r$row -resize expand
2107    blt::table configure $inner c3 -resize expand
2108    event generate [winfo parent [winfo parent $w]] <Configure>
2109}
2110
2111itcl::body Rappture::NanovisViewer::ToggleVolume { tag name } {
2112    set bool $_settings($this-volume-$name)
2113    SendCmd "volume state $bool $name"
2114}
2115
2116itcl::body Rappture::NanovisViewer::SetOrientation { side } {
2117    array set positions {
2118        front "1 0 0 0"
2119        back  "0 0 1 0"
2120        left  "0.707107 0 -0.707107 0"
2121        right "0.707107 0 0.707107 0"
2122        top   "0.707107 -0.707107 0 0"
2123        bottom "0.707107 0.707107 0 0"
2124    }
2125    foreach name { qw qx qy qz } value $positions($side) {
2126        set _view($name) $value
2127    }
2128    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
2129    $_arcball quaternion $q
2130    SendCmd "camera orient $q"
2131    SendCmd "camera reset"
2132    set _view(xpan) 0
2133    set _view(ypan) 0
2134    set _view(zoom) 1.0
2135    set _settings($this-xpan) $_view(xpan)
2136    set _settings($this-ypan) $_view(ypan)
2137    set _settings($this-zoom) $_view(zoom)
2138}
2139
2140
2141#
2142# InitComponentSettings --
2143#
2144#       Initializes the volume settings for a specific component. This
2145#       should match what's used as global settings above. This
2146#       is called the first time we try to switch to a given component
2147#       in SwitchComponent below.
2148#
2149itcl::body Rappture::NanovisViewer::InitComponentSettings { cname } {
2150    array set _settings [subst {
2151        $cname-ambient           60
2152        $cname-colormap          default
2153        $cname-diffuse           40
2154        $cname-light2side        1
2155        $cname-opacity           100
2156        $cname-outline           0
2157        $cname-specularExponent  90
2158        $cname-specularLevel     30
2159        $cname-thickness         350
2160        $cname-transp            50
2161        $cname-volumeVisible     1
2162    }]
2163}
2164
2165#
2166# SwitchComponent --
2167#
2168#       This is called when the current component is changed by the
2169#       dropdown menu in the volume tab.  It synchronizes the global
2170#       volume settings with the settings of the new current component.
2171#
2172itcl::body Rappture::NanovisViewer::SwitchComponent { cname } {
2173    if { ![info exists _settings($cname-ambient)] } {
2174        InitComponentSettings $cname
2175    }
2176    # _settings variables change widgets, except for colormap
2177    set _settings($this-ambient)          $_settings($cname-ambient)
2178    set _settings($this-colormap)         $_settings($cname-colormap)
2179    set _settings($this-diffuse)          $_settings($cname-diffuse)
2180    set _settings($this-light2side)       $_settings($cname-light2side)
2181    set _settings($this-opacity)          $_settings($cname-opacity)
2182    set _settings($this-outline)          $_settings($cname-outline)
2183    set _settings($this-specularExponent) $_settings($cname-specularExponent)
2184    set _settings($this-specularLevel)    $_settings($cname-specularLevel)
2185    set _settings($this-thickness)        $_settings($cname-thickness)
2186    set _settings($this-transp)           $_settings($cname-transp)
2187    set _settings($this-volumeVisible)    $_settings($cname-volumeVisible)
2188    $itk_component(colormap) value        $_settings($cname-colormap)
2189    set _current $cname;                # Reset the current component
2190}
2191
2192#
2193# BuildVolumeComponents --
2194#
2195#       This is called from the "scale" method which is called when a
2196#       new dataset is added or deleted.  It repopulates the dropdown
2197#       menu of volume component names.  It sets the current component
2198#       to the first component in the list (of components found).
2199#       Finally, if there is only one component, don't display the
2200#       label or the combobox in the volume settings tab.
2201#
2202itcl::body Rappture::NanovisViewer::BuildVolumeComponents {} {
2203    $itk_component(volcomponents) choices delete 0 end
2204    foreach name $_componentsList {
2205        $itk_component(volcomponents) choices insert end $name $name
2206    }
2207    set _current [lindex $_componentsList 0]
2208    $itk_component(volcomponents) value $_current
2209    set parent [winfo parent $itk_component(volcomponents)]
2210    if { [llength $_componentsList] <= 1 } {
2211        # Unpack the components label and dropdown if there's only one
2212        # component.
2213        blt::table forget $parent.volcomponents_l $parent.volcomponents
2214    } else {
2215        # Pack the components label and dropdown into the table there's
2216        # more than one component to select.
2217        blt::table $parent \
2218            0,0 $parent.volcomponents_l -anchor e -cspan 2 \
2219            0,2 $parent.volcomponents -cspan 3 -fill x
2220    }
2221}
2222
2223#
2224# GetDatasetsWithComponents --
2225#
2226#       Returns a list of all the datasets (known by the combination of
2227#       their data object and component name) that match the given
2228#       component name.  For example, this is used where we want to change
2229#       the settings of volumes that have the current component.
2230#
2231itcl::body Rappture::NanovisViewer::GetDatasetsWithComponent { cname } {
2232    if { ![info exists _volcomponents($cname)] } {
2233        return ""
2234    }
2235    set list ""
2236    foreach tag $_volcomponents($cname) {
2237        if { ![info exists _serverDatasets($tag)] } {
2238            continue
2239        }
2240        lappend list $tag
2241    }
2242    return $list
2243}
2244
2245#
2246# HideAllMarkers --
2247#
2248#       Hide all the markers in all the transfer functions.  Can't simply
2249#       delete and recreate markers from the <style> since the user may
2250#       have create, deleted, or moved markers.
2251#
2252itcl::body Rappture::NanovisViewer::HideAllMarkers {} {
2253    foreach cname [array names _transferFunctionEditors] {
2254        $_transferFunctionEditors($cname) hideMarkers
2255    }
2256}
2257
2258itcl::body Rappture::NanovisViewer::GetColormap { cname color } {
2259    if { $color == "default" } {
2260        return $_cname2defaultcolormap($cname)
2261    }
2262    return [ColorsToColormap $color]
2263}
2264
2265itcl::body Rappture::NanovisViewer::GetAlphamap { cname name } {
2266    if { $name == "default" } {
2267        return $_cname2defaultalphamap($cname)
2268    }
2269    return [NameToAlphamap $name]
2270}
2271
2272itcl::body Rappture::NanovisViewer::ResetColormap { cname color } {
2273    # Get the current transfer function
2274    if { ![info exists _cname2transferFunction($cname)] } {
2275        return
2276    }
2277    foreach { cmap wmap } $_cname2transferFunction($cname) break
2278    set cmap [GetColormap $cname $color]
2279    set _cname2transferFunction($cname) [list $cmap $wmap]
2280    SendCmd [list transfunc define $cname $cmap $wmap]
2281    EventuallyRedrawLegend
2282}
2283
2284itcl::body Rappture::NanovisViewer::ComputeAlphamap { cname } {
2285    if { ![info exists _transferFunctionEditors($cname)] } {
2286        return [list 0.0 0.0 1.0 1.0]
2287    }
2288    if { ![info exists _settings($cname-ambient)] } {
2289        InitComponentSettings $cname
2290    }
2291    set max 1.0 ;                       #$_settings($tag-opacity)
2292
2293    set isovalues [$_transferFunctionEditors($cname) values]
2294
2295    # Ensure that the global opacity and thickness settings (in the slider
2296    # settings widgets) are used for the active transfer-function.  Update
2297    # the values in the _settings varible.
2298    set opacity [expr { double($_settings($cname-opacity)) * 0.01 }]
2299
2300    # Scale values between 0.00001 and 0.01000
2301    set delta [expr {double($_settings($cname-thickness)) * 0.0001}]
2302   
2303    set first [lindex $isovalues 0]
2304    set last [lindex $isovalues end]
2305    set wmap ""
2306    if { $first == "" || $first != 0.0 } {
2307        lappend wmap 0.0 0.0
2308    }
2309    foreach x $isovalues {
2310        set x1 [expr {$x-$delta-0.00001}]
2311        set x2 [expr {$x-$delta}]
2312        set x3 [expr {$x+$delta}]
2313        set x4 [expr {$x+$delta+0.00001}]
2314        if { $x1 < 0.0 } {
2315            set x1 0.0
2316        } elseif { $x1 > 1.0 } {
2317            set x1 1.0
2318        }
2319        if { $x2 < 0.0 } {
2320            set x2 0.0
2321        } elseif { $x2 > 1.0 } {
2322            set x2 1.0
2323        }
2324        if { $x3 < 0.0 } {
2325            set x3 0.0
2326        } elseif { $x3 > 1.0 } {
2327            set x3 1.0
2328        }
2329        if { $x4 < 0.0 } {
2330            set x4 0.0
2331        } elseif { $x4 > 1.0 } {
2332            set x4 1.0
2333        }
2334        # add spikes in the middle
2335        lappend wmap $x1 0.0
2336        lappend wmap $x2 $max
2337        lappend wmap $x3 $max
2338        lappend wmap $x4 0.0
2339    }
2340    if { $last == "" || $last != 1.0 } {
2341        lappend wmap 1.0 0.0
2342    }
2343    return $wmap
2344}
2345
2346
2347itcl::body Rappture::NanovisViewer::NameToAlphamap { name } {
2348    switch -- $name {
2349        "ramp-up" {
2350            set wmap {
2351                0.0 0.0
2352                1.0 1.0
2353            }
2354        }
2355        "ramp-down" {
2356            set wmap {
2357                0.0 1.0
2358                1.0 0.0
2359            }
2360        }
2361        "vee" {
2362            set wmap {
2363                0.0 1.0
2364                0.5 0.0
2365                1.0 1.0
2366            }
2367        }
2368        "tent-1" {
2369            set wmap {
2370                0.0 0.0
2371                0.5 1.0
2372                1.0 0.0
2373            }
2374        }
2375        "tent-2" {
2376            set wmap {
2377                0.0 0.0
2378                0.25 1.0
2379                0.5 0.0
2380                0.75 1.0
2381                1.0 0.0
2382            }
2383        }
2384        "tent-3" {
2385            set wmap {
2386                0.0 0.0
2387                0.16666 1.0
2388                0.33333 0.0
2389                0.5     1.0
2390                0.66666 0.0
2391                0.83333 1.0
2392                1.0 0.0
2393            }
2394        }
2395        "tent-4" {
2396            set wmap {
2397                0.0     0.0
2398                0.125   1.0
2399                0.25    0.0
2400                0.375   1.0
2401                0.5     0.0       
2402                0.625   1.0
2403                0.75    0.0
2404                0.875   1.0
2405                1.0     0.0
2406            }
2407        }
2408        "sinusoid-1" {
2409            set wmap {
2410                0.0                     0.000 0.600 0.800
2411                0.14285714285714285     0.400 0.900 1.000
2412                0.2857142857142857      0.600 1.000 1.000
2413                0.42857142857142855     0.800 1.000 1.000
2414                0.5714285714285714      0.900 0.900 0.900
2415                0.7142857142857143      0.600 0.600 0.600
2416                0.8571428571428571      0.400 0.400 0.400
2417                1.0                     0.200 0.200 0.200
2418            }
2419        }
2420        "sinusoid-2" {
2421            set wmap {
2422                0.0                     0.900 1.000 1.000
2423                0.1111111111111111      0.800 0.983 1.000
2424                0.2222222222222222      0.700 0.950 1.000
2425                0.3333333333333333      0.600 0.900 1.000
2426                0.4444444444444444      0.500 0.833 1.000
2427                0.5555555555555556      0.400 0.750 1.000
2428                0.6666666666666666      0.300 0.650 1.000
2429                0.7777777777777778      0.200 0.533 1.000
2430                0.8888888888888888      0.100 0.400 1.000
2431                1.0                     0.000 0.250 1.000
2432            }
2433        }
2434        "sinusoid-6" {
2435            set wmap {
2436                0.0                             0.200   0.100   0.000
2437                0.09090909090909091             0.400   0.187   0.000
2438                0.18181818181818182             0.600   0.379   0.210
2439                0.2727272727272727              0.800   0.608   0.480
2440                0.36363636363636365             0.850   0.688   0.595
2441                0.45454545454545453             0.950   0.855   0.808
2442                0.5454545454545454              0.800   0.993   1.000
2443                0.6363636363636364              0.600   0.973   1.000
2444                0.7272727272727273              0.400   0.940   1.000
2445                0.8181818181818182              0.200   0.893   1.000
2446                0.9090909090909091              0.000   0.667   0.800
2447                1.0                             0.000   0.480   0.600
2448            }
2449        }
2450        "sinusoid-10" {
2451            set wmap {
2452                0.0                             0.000   0.480   0.600
2453                0.09090909090909091             0.000   0.667   0.800
2454                0.18181818181818182             0.200   0.893   1.000
2455                0.2727272727272727              0.400   0.940   1.000
2456                0.36363636363636365             0.600   0.973   1.000
2457                0.45454545454545453             0.800   0.993   1.000
2458                0.5454545454545454              0.950   0.855   0.808
2459                0.6363636363636364              0.850   0.688   0.595
2460                0.7272727272727273              0.800   0.608   0.480
2461                0.8181818181818182              0.600   0.379   0.210
2462                0.9090909090909091              0.400   0.187   0.000
2463                1.0                             0.200   0.100   0.000
2464            }
2465        }
2466        "step-2" {
2467            set wmap {
2468                0.0                             0.000   0.167   1.000
2469                0.09090909090909091             0.100   0.400   1.000
2470                0.18181818181818182             0.200   0.600   1.000
2471                0.2727272727272727              0.400   0.800   1.000
2472                0.36363636363636365             0.600   0.933   1.000
2473                0.45454545454545453             0.800   1.000   1.000
2474                0.5454545454545454              1.000   1.000   0.800
2475                0.6363636363636364              1.000   0.933   0.600
2476                0.7272727272727273              1.000   0.800   0.400
2477                0.8181818181818182              1.000   0.600   0.200
2478                0.9090909090909091              1.000   0.400   0.100
2479                1.0                             1.000   0.167   0.000
2480            }
2481        }
2482        "step-5" {
2483            set wmap {
2484                0.0                             1.000   0.167   0.000
2485                0.09090909090909091             1.000   0.400   0.100
2486                0.18181818181818182             1.000   0.600   0.200
2487                0.2727272727272727              1.000   0.800   0.400
2488                0.36363636363636365             1.000   0.933   0.600
2489                0.45454545454545453             1.000   1.000   0.800
2490                0.5454545454545454              0.800   1.000   1.000
2491                0.6363636363636364              0.600   0.933   1.000
2492                0.7272727272727273              0.400   0.800   1.000
2493                0.8181818181818182              0.200   0.600   1.000
2494                0.9090909090909091              0.100   0.400   1.000
2495                1.0                             0.000   0.167   1.000
2496            }
2497        }
2498        "step-12" {
2499            set wmap {
2500                "#EE82EE"
2501                "#4B0082"
2502                "blue"
2503                "#008000"
2504                "yellow"
2505                "#FFA500"
2506                "red"
2507            }
2508        }
2509        default {
2510        }
2511    }
2512    # Multiply each component by the global opacity value.
2513    return ""
2514}
Note: See TracBrowser for help on using the repository browser.