source: branches/r9/gui/scripts/nanovisviewer.tcl @ 4919

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