source: branches/1.4/gui/scripts/nanovisviewer.tcl @ 5301

Last change on this file since 5301 was 5301, checked in by ldelgass, 4 years ago

Revert change from r4662, this isn't needed since setting the value of the
colormap combobox will cause the legend to be redrawn. This syncs up with the
trunk version.

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