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

Last change on this file since 4662 was 4662, checked in by gah, 7 years ago

update legend after switching component

File size: 87.3 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          0.5
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 "hub" [exec hostname]
679            lappend info "client" "nanovisviewer"
680            lappend info "user" $user
681            lappend info "session" $session
682            SendCmd "clientinfo [list $info]"
683        }
684
685        set w [winfo width $itk_component(3dview)]
686        set h [winfo height $itk_component(3dview)]
687        EventuallyResize $w $h
688    }
689    return $result
690}
691
692#
693# isconnected --
694#
695#       Indicates if we are currently connected to the visualization server.
696#
697itcl::body Rappture::NanovisViewer::isconnected {} {
698    return [VisViewer::IsConnected]
699}
700
701#
702# disconnect --
703#
704itcl::body Rappture::NanovisViewer::disconnect {} {
705    Disconnect
706}
707
708#
709# Disconnect --
710#
711#       Clients use this method to disconnect from the current rendering
712#       server.
713#
714itcl::body Rappture::NanovisViewer::Disconnect {} {
715    VisViewer::Disconnect
716
717    # disconnected -- no more data sitting on server
718    array unset _serverDatasets
719}
720
721# ----------------------------------------------------------------------
722# USAGE: SendTransferFunctions
723# ----------------------------------------------------------------------
724itcl::body Rappture::NanovisViewer::SendTransferFunctions {} {
725    foreach cname [array names _volcomponents] {
726        ComputeTransferFunction $cname
727    }
728    FixLegend
729}
730
731# ----------------------------------------------------------------------
732# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
733#
734# Invoked automatically whenever the "image" command comes in from
735# the rendering server.  Indicates that binary image data with the
736# specified <size> will follow.
737# ----------------------------------------------------------------------
738itcl::body Rappture::NanovisViewer::ReceiveImage { args } {
739    array set info {
740        -token "???"
741        -bytes 0
742        -type image
743    }
744    array set info $args
745    set bytes [ReceiveBytes $info(-bytes)]
746    ReceiveEcho <<line "<read $info(-bytes) bytes"
747    if { $info(-type) == "image" } {
748        ReceiveEcho "for [image width $_image(plot)]x[image height $_image(plot)] image>"       
749        $_image(plot) configure -data $bytes
750    } elseif { $info(-type) == "print" } {
751        set tag $this-print-$info(-token)
752        set _hardcopy($tag) $bytes
753    }
754}
755
756#
757# DrawLegend --
758#
759itcl::body Rappture::NanovisViewer::DrawLegend { cname } {
760    set c $itk_component(legend)
761    set w [winfo width $c]
762    set h [winfo height $c]
763    set lx 10
764    set ly [expr {$h - 1}]
765    if {"" == [$c find withtag colorbar]} {
766        $c create image 10 10 -anchor nw \
767            -image $_image(legend) -tags colorbar
768        $c create text $lx $ly -anchor sw \
769            -fill $itk_option(-plotforeground) -tags "limits text vmin"
770        $c create text [expr {$w-$lx}] $ly -anchor se \
771            -fill $itk_option(-plotforeground) -tags "limits text vmax"
772        $c create text [expr {$w/2}] $ly -anchor s \
773            -fill $itk_option(-plotforeground) -tags "limits text title"
774        $c lower colorbar
775        $c bind colorbar <ButtonRelease-1> [itcl::code $this AddNewMarker %x %y]
776    }
777
778    # Display the markers used by the current transfer function.
779    HideAllMarkers
780    $_transferFunctionEditors($cname) showMarkers $_limits($cname)
781
782    foreach {min max} $_limits($cname) break
783    $c itemconfigure vmin -text [format %g $min]
784    $c coords vmin $lx $ly
785
786    $c itemconfigure vmax -text [format %g $max]
787    $c coords vmax [expr {$w-$lx}] $ly
788
789    set title [$_first hints label]
790    set units [$_first hints units]
791    if { $units != "" } {
792        set title "$title ($units)"
793    }
794    $c itemconfigure title -text $title
795    $c coords title [expr {$w/2}] $ly
796
797    # The colormap may have changed. Resync the slicers with the colormap.
798    InitSettings -cutplanesvisible -xcutplanevisible -ycutplanevisible \
799        -zcutplanevisible
800}
801
802#
803#
804# ReceiveLegend --
805#
806#       The procedure is the response from the render server to each "legend"
807#       command.  The server sends back a "legend" command invoked our
808#       the slave interpreter.  The purpose is to collect data of the image
809#       representing the legend in the canvas.  In addition, the
810#       active transfer function is displayed.
811#
812#
813itcl::body Rappture::NanovisViewer::ReceiveLegend { cname vmin vmax size } {
814    if { ![isconnected] } {
815        return
816    }
817    set bytes [ReceiveBytes $size]
818    $_image(legend) configure -data $bytes
819    ReceiveEcho <<line "<read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>"
820
821    DrawLegend $_current
822}
823
824#
825# ReceiveData --
826#
827#       The procedure is the response from the render server to each "data
828#       follows" command.  The server sends back a "data" command invoked our
829#       the slave interpreter.  The purpose is to collect the min/max of the
830#       volume sent to the render server.  Since the client (nanovisviewer)
831#       doesn't parse 3D data formats, we rely on the server (nanovis) to
832#       tell us what the limits are.  Once we've received the limits to all
833#       the data we've sent (tracked by _recvdDatasets) we can then determine
834#       what the transfer functions are for these volumes.
835#
836#
837#       Note: There is a considerable tradeoff in having the server report
838#             back what the data limits are.  It means that much of the code
839#             having to do with transfer-functions has to wait for the data
840#             to come back, since the isomarkers are calculated based upon
841#             the data limits.  The client code is much messier because of
842#             this.  The alternative is to parse any of the 3D formats on the
843#             client side.
844#
845itcl::body Rappture::NanovisViewer::ReceiveData { args } {
846    if { ![isconnected] } {
847        return
848    }
849
850    # Arguments from server are name value pairs. Stuff them in an array.
851    array set info $args
852
853    set tag $info(tag)
854    set parts [split $tag -]
855
856    #
857    # Volumes don't exist until we're told about them.
858    #
859    set dataobj [lindex $parts 0]
860    set _serverDatasets($tag) 1
861    if { $_settings(-volumevisible) && $dataobj == $_first } {
862        SendCmd "volume state 1 $tag"
863    }
864    set _limits($tag) [list $info(min)  $info(max)]
865    set _limits(v)    [list $info(vmin) $info(vmax)]
866
867    unset _recvdDatasets($tag)
868    if { [array size _recvdDatasets] == 0 } {
869        updateTransferFunctions
870    }
871}
872
873# ----------------------------------------------------------------------
874# USAGE: Rebuild
875#
876# Called automatically whenever something changes that affects the
877# data in the widget.  Clears any existing data and rebuilds the
878# widget to display new data.
879# ----------------------------------------------------------------------
880itcl::body Rappture::NanovisViewer::Rebuild {} {
881    set w [winfo width $itk_component(3dview)]
882    set h [winfo height $itk_component(3dview)]
883    if { $w < 2 || $h < 2 } {
884        $_dispatcher event -idle !rebuild
885        return
886    }
887
888    # Turn on buffering of commands to the server.  We don't want to
889    # be preempted by a server disconnect/reconnect (which automatically
890    # generates a new call to Rebuild).   
891    StartBufferingCommands
892
893    if { $_width != $w || $_height != $h || $_reset } {
894        set _width $w
895        set _height $h
896        $_arcball resize $w $h
897        DoResize
898    }
899
900    foreach dataobj [get] {
901        foreach cname [$dataobj components] {
902            set tag $dataobj-$cname
903            if { ![info exists _serverDatasets($tag)] } {
904                # Send the data as one huge base64-encoded mess -- yuck!
905                if { [$dataobj type] == "dx" } {
906                    if { ![$dataobj isvalid] } {
907                        puts stderr "??? $dataobj is invalid"
908                    }
909                    set data [$dataobj blob $cname]
910                } else {
911                    set data [$dataobj vtkdata $cname]
912                    if 0 {
913                        set f [open "/tmp/volume.vtk" "w"]
914                        puts $f $data
915                        close $f
916                    }
917                }
918                set nbytes [string length $data]
919                if { $_reportClientInfo }  {
920                    set info {}
921                    lappend info "tool_id"       [$dataobj hints toolId]
922                    lappend info "tool_name"     [$dataobj hints toolName]
923                    lappend info "tool_version"  [$dataobj hints toolRevision]
924                    lappend info "tool_title"    [$dataobj hints toolTitle]
925                    lappend info "dataset_label" [$dataobj hints label]
926                    lappend info "dataset_size"  $nbytes
927                    lappend info "dataset_tag"   $tag
928                    SendCmd "clientinfo [list $info]"
929                }
930                SendCmd "volume data follows $nbytes $tag"
931                append _outbuf $data
932                set _recvdDatasets($tag) 1
933                set _serverDatasets($tag) 0
934            }
935            SetObjectStyle $dataobj $cname
936        }
937    }
938
939    # Outline seems to need to be reset every update.
940    InitSettings -outlinevisible -cutplanesvisible -current
941
942    set _first [lindex [get] 0]
943    if { $_reset } {
944        #
945        # Reset the camera and other view parameters
946        #
947        set _settings(-qw)    $_view(-qw)
948        set _settings(-qx)    $_view(-qx)
949        set _settings(-qy)    $_view(-qy)
950        set _settings(-qz)    $_view(-qz)
951        set _settings(-xpan)  $_view(-xpan)
952        set _settings(-ypan)  $_view(-ypan)
953        set _settings(-zoom)  $_view(-zoom)
954
955        set q [ViewToQuaternion]
956        $_arcball quaternion $q
957        SendCmd "camera orient $q"
958        SendCmd "camera reset"
959        PanCamera
960        SendCmd "camera zoom $_view(-zoom)"
961       
962        #cutplane state 0 all
963        foreach axis {x y z} {
964            # Turn off cutplanes for all volumes
965            SendCmd "cutplane state 0 $axis"
966        }
967
968        InitSettings -light2side -light -volumeopacity \
969            -isosurfaceshading -gridvisible -axesvisible \
970
971        if {"" != $_first} {
972            set axis [$_first hints updir]
973            if { "" != $axis } {
974                SendCmd "up $axis"
975            }
976            set location [$_first hints camera]
977            if { $location != "" } {
978                array set _view $location
979            }
980        }
981    }
982
983    # nothing to send -- activate the proper ivol
984    SendCmd "volume state 0"
985    if {"" != $_first} {
986        set datasets [array names _serverDatasets $_first-*]
987        if { $datasets != "" } {
988            SendCmd "volume state 1 $datasets"
989        }
990        # If the first volume already exists on the server, then make sure
991        # we display the proper transfer function in the legend.
992        set cname [lindex [$_first components] 0]
993        if { [info exists _serverDatasets($_first-$cname)] } {
994            updateTransferFunctions
995        }
996    }
997    # Actually write the commands to the server socket.  If it fails, we don't
998    # care.  We're finished here.
999    blt::busy hold $itk_component(hull)
1000    StopBufferingCommands
1001    blt::busy release $itk_component(hull)
1002    set _reset 0
1003}
1004
1005# ----------------------------------------------------------------------
1006# USAGE: CurrentDatasets ?-cutplanes?
1007#
1008# Returns a list of volume server IDs for the current volume being
1009# displayed.  This is normally a single ID, but it might be a list
1010# of IDs if the current data object has multiple components.
1011# ----------------------------------------------------------------------
1012itcl::body Rappture::NanovisViewer::CurrentDatasets {{what -all}} {
1013    set rlist ""
1014    if { $_first == "" } {
1015        return
1016    }
1017    foreach cname [$_first components] {
1018        set tag $_first-$cname
1019        if { [info exists _serverDatasets($tag)] && $_serverDatasets($tag) } {
1020            array set styles {
1021                -cutplanes 1
1022            }
1023            array set styles [lindex [$_first components -style $cname] 0]
1024            if { $what != "-cutplanes" || $styles(-cutplanes) } {
1025                lappend rlist $tag
1026            }
1027        }
1028    }
1029    return $rlist
1030}
1031
1032# ----------------------------------------------------------------------
1033# USAGE: Zoom in
1034# USAGE: Zoom out
1035# USAGE: Zoom reset
1036#
1037# Called automatically when the user clicks on one of the zoom
1038# controls for this widget.  Changes the zoom for the current view.
1039# ----------------------------------------------------------------------
1040itcl::body Rappture::NanovisViewer::Zoom {option} {
1041    switch -- $option {
1042        "in" {
1043            set _view(-zoom) [expr {$_view(-zoom)*1.25}]
1044            set _settings(-zoom) $_view(-zoom)
1045            SendCmd "camera zoom $_view(-zoom)"
1046        }
1047        "out" {
1048            set _view(-zoom) [expr {$_view(-zoom)*0.8}]
1049            set _settings(-zoom) $_view(-zoom)
1050            SendCmd "camera zoom $_view(-zoom)"
1051        }
1052        "reset" {
1053            array set _view {
1054                -qw      0.853553
1055                -qx      -0.353553
1056                -qy      0.353553
1057                -qz      0.146447
1058                -xpan    0
1059                -ypan    0
1060                -zoom    1.0
1061            }
1062            if { $_first != "" } {
1063                set location [$_first hints camera]
1064                if { $location != "" } {
1065                    array set _view $location
1066                }
1067            }
1068            set q [ViewToQuaternion]         
1069            $_arcball quaternion $q
1070            SendCmd "camera orient $q"
1071            SendCmd "camera reset"
1072            set _settings(-qw)    $_view(-qw)
1073            set _settings(-qx)    $_view(-qx)
1074            set _settings(-qy)    $_view(-qy)
1075            set _settings(-qz)    $_view(-qz)
1076            set _settings(-xpan)  $_view(-xpan)
1077            set _settings(-ypan)  $_view(-ypan)
1078            set _settings(-zoom)  $_view(-zoom)
1079        }
1080    }
1081}
1082
1083itcl::body Rappture::NanovisViewer::PanCamera {} {
1084    set x $_view(-xpan)
1085    set y $_view(-ypan)
1086    SendCmd "camera pan $x $y"
1087}
1088
1089
1090# ----------------------------------------------------------------------
1091# USAGE: Rotate click <x> <y>
1092# USAGE: Rotate drag <x> <y>
1093# USAGE: Rotate release <x> <y>
1094#
1095# Called automatically when the user clicks/drags/releases in the
1096# plot area.  Moves the plot according to the user's actions.
1097# ----------------------------------------------------------------------
1098itcl::body Rappture::NanovisViewer::Rotate {option x y} {
1099    switch -- $option {
1100        click {
1101            $itk_component(3dview) configure -cursor fleur
1102            set _click(x) $x
1103            set _click(y) $y
1104        }
1105        drag {
1106            if {[array size _click] == 0} {
1107                Rotate click $x $y
1108            } else {
1109                set w [winfo width $itk_component(3dview)]
1110                set h [winfo height $itk_component(3dview)]
1111                if {$w <= 0 || $h <= 0} {
1112                    return
1113                }
1114
1115                if {[catch {
1116                    # this fails sometimes for no apparent reason
1117                    set dx [expr {double($x-$_click(x))/$w}]
1118                    set dy [expr {double($y-$_click(y))/$h}]
1119                }]} {
1120                    return
1121                }
1122
1123                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1124                foreach { _view(-qw) _view(-qx) _view(-qy) _view(-qz) } $q break
1125                set _settings(-qw) $_view(-qw)
1126                set _settings(-qx) $_view(-qx)
1127                set _settings(-qy) $_view(-qy)
1128                set _settings(-qz) $_view(-qz)
1129                SendCmd "camera orient $q"
1130
1131                set _click(x) $x
1132                set _click(y) $y
1133            }
1134        }
1135        release {
1136            Rotate drag $x $y
1137            $itk_component(3dview) configure -cursor ""
1138            catch {unset _click}
1139        }
1140        default {
1141            error "bad option \"$option\": should be click, drag, release"
1142        }
1143    }
1144}
1145
1146# ----------------------------------------------------------------------
1147# USAGE: $this Pan click x y
1148#        $this Pan drag x y
1149#        $this Pan release x y
1150#
1151# Called automatically when the user clicks on one of the zoom
1152# controls for this widget.  Changes the zoom for the current view.
1153# ----------------------------------------------------------------------
1154itcl::body Rappture::NanovisViewer::Pan {option x y} {
1155    # Experimental stuff
1156    set w [winfo width $itk_component(3dview)]
1157    set h [winfo height $itk_component(3dview)]
1158    if { $option == "set" } {
1159        set x [expr $x / double($w)]
1160        set y [expr $y / double($h)]
1161        set _view(-xpan) [expr $_view(-xpan) + $x]
1162        set _view(-ypan) [expr $_view(-ypan) + $y]
1163        PanCamera
1164        set _settings(-xpan) $_view(-xpan)
1165        set _settings(-ypan) $_view(-ypan)
1166        return
1167    }
1168    if { $option == "click" } {
1169        set _click(x) $x
1170        set _click(y) $y
1171        $itk_component(3dview) configure -cursor hand1
1172    }
1173    if { $option == "drag" || $option == "release" } {
1174        set dx [expr ($_click(x) - $x)/double($w)]
1175        set dy [expr ($_click(y) - $y)/double($h)]
1176        set _click(x) $x
1177        set _click(y) $y
1178        set _view(-xpan) [expr $_view(-xpan) - $dx]
1179        set _view(-ypan) [expr $_view(-ypan) - $dy]
1180        PanCamera
1181        set _settings(-xpan) $_view(-xpan)
1182        set _settings(-ypan) $_view(-ypan)
1183    }
1184    if { $option == "release" } {
1185        $itk_component(3dview) configure -cursor ""
1186    }
1187}
1188
1189# ----------------------------------------------------------------------
1190# USAGE: InitSettings <what> ?<value>?
1191#
1192# Used internally to update rendering settings whenever parameters
1193# change in the popup settings panel.  Sends the new settings off
1194# to the back end.
1195# ----------------------------------------------------------------------
1196itcl::body Rappture::NanovisViewer::InitSettings { args } {
1197    foreach arg $args {
1198        AdjustSetting $arg
1199    }
1200}
1201
1202# ----------------------------------------------------------------------
1203# USAGE: AdjustSetting <what> ?<value>?
1204#
1205# Used internally to update rendering settings whenever parameters
1206# change in the popup settings panel.  Sends the new settings off
1207# to the back end.
1208# ----------------------------------------------------------------------
1209itcl::body Rappture::NanovisViewer::AdjustSetting {what {value ""}} {
1210    if {![isconnected]} {
1211        return
1212    }
1213    switch -- $what {
1214        "-axesvisible" {
1215            SendCmd "axis visible $_settings($what)"
1216        }
1217        "-background" {
1218            set bgcolor [$itk_component(background) value]
1219            array set fgcolors {
1220                "black" "white"
1221                "white" "black"
1222                "grey"  "black"
1223            }
1224            configure -plotbackground $bgcolor \
1225                -plotforeground $fgcolors($bgcolor)
1226            DrawLegend $_current
1227        }
1228        "-colormap" {
1229            set color [$itk_component(colormap) value]
1230            set _settings($what) $color
1231            set _settings($_current${what}) $color
1232            ResetColormap $_current $color
1233        }
1234        "-current" {
1235            set cname [$itk_component(volcomponents) value]
1236            SwitchComponent $cname
1237        }
1238        "-cutplanesvisible" {
1239            set bool $_settings($what)
1240            # We only set cutplanes on the first dataset.
1241            set datasets [CurrentDatasets -cutplanes]
1242            set tag [lindex $datasets 0]
1243            if { $bool } {
1244                foreach axis { x y z } {
1245                    if { $_settings(-${axis}cutplanevisible) } {
1246                        SendCmd "cutplane state 1 $axis $tag"
1247                    }
1248                }
1249            } else {
1250                foreach axis { x y z } {
1251                    SendCmd "cutplane state 0 $axis $tag"
1252                }
1253            }
1254        }
1255        "-gridvisible" {
1256            SendCmd "grid visible $_settings($what)"
1257        }
1258        "-isosurfaceshading" {
1259            SendCmd "volume shading isosurface $_settings($what)"
1260        }
1261        "-legendvisible" {
1262            if { $_settings($what) } {
1263                blt::table $itk_component(plotarea) \
1264                    0,0 $itk_component(3dview) -fill both \
1265                    1,0 $itk_component(legend) -fill x
1266                blt::table configure $itk_component(plotarea) r1 -resize none
1267            } else {
1268                blt::table forget $itk_component(legend)
1269            }
1270        }
1271        "-light" {
1272            set _settings($_current${what}) $_settings($what)
1273            set val $_settings($what)
1274            set diffuse [expr {0.01*$val}]
1275            set ambient [expr {1.0-$diffuse}]
1276            set specularLevel 0.3
1277            set specularExp 90.0
1278            foreach tag [GetDatasetsWithComponent $_current] {
1279                SendCmd "volume shading ambient $ambient $tag"
1280                SendCmd "volume shading diffuse $diffuse $tag"
1281                SendCmd "volume shading specularLevel $specularLevel $tag"
1282                SendCmd "volume shading specularExp $specularExp $tag"
1283            }
1284        }
1285        "-light2side" {
1286            set _settings($_current${what}) $_settings($what)
1287            set val $_settings($what)
1288            foreach tag [GetDatasetsWithComponent $_current] {
1289                SendCmd "volume shading light2side $val"
1290            }
1291        }
1292        "-outlinevisible" {
1293            SendCmd "volume outline state $_settings($what)"
1294        }
1295        "-thickness" {
1296            set val $_settings($what)
1297            set _settings($_current${what}) $val
1298            updateTransferFunctions
1299        }
1300        "-volume" {
1301            # This is the global volume visibility control.  It controls the
1302            # visibility of all the all volumes.  Whenever it's changed, you
1303            # have to synchronize each of the local controls (see below) with
1304            # this.
1305            set datasets [CurrentDatasets]
1306            set bool $_settings($what)
1307            SendCmd "volume data state $bool $datasets"
1308            foreach cname $_componentsList {
1309                set _settings($cname-volumevisible) $bool
1310            }
1311            set _settings(-volumevisible) $bool
1312        }
1313        "-volumeopacity" {
1314            set _settings($what) [expr $_widget($what) * 0.01]
1315            set _settings($_current${what}) $_settings($what)
1316            foreach tag [GetDatasetsWithComponent $_current] {
1317                SendCmd "volume shading opacity $_settings($what) $tag"
1318            }
1319        }
1320        "-volumevisible" {
1321            # This is the component specific control.  It changes the
1322            # visibility of only the current component.
1323            set _settings($_current${what}) $_settings($what)
1324            foreach tag [GetDatasetsWithComponent $_current] {
1325                SendCmd "volume data state $_settings($what) $tag"
1326            }
1327        }
1328        "-xcutplanevisible" - "-ycutplanevisible" - "-zcutplanevisible" {
1329            set axis [string range $what 1 1]
1330            set bool $_settings($what)
1331            # We only set cutplanes on the first dataset.
1332            set datasets [CurrentDatasets -cutplanes]
1333            set tag [lindex $datasets 0]
1334            if { $_settings(-cutplanesvisible) } {
1335                SendCmd "cutplane state $bool $axis $tag"
1336            }
1337            if { $bool } {
1338                $itk_component(${axis}CutScale) configure -state normal \
1339                    -troughcolor white
1340            } else {
1341                $itk_component(${axis}CutScale) configure -state disabled \
1342                    -troughcolor grey82
1343            }
1344        }
1345        default {
1346            error "don't know how to fix $what"
1347        }
1348    }
1349}
1350
1351# ----------------------------------------------------------------------
1352# USAGE: FixLegend
1353#
1354# Used internally to update the legend area whenever it changes size
1355# or when the field changes.  Asks the server to send a new legend
1356# for the current field.
1357# ----------------------------------------------------------------------
1358itcl::body Rappture::NanovisViewer::FixLegend {} {
1359    set _resizeLegendPending 0
1360    set lineht [font metrics $itk_option(-font) -linespace]
1361    set w [expr {$_width-20}]
1362    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
1363    if {$w > 0 && $h > 0 && $_first != "" } {
1364        if { [info exists _cname2transferFunction($_current)] } {
1365            SendCmd "legend $_current $w $h"
1366        }
1367    }
1368}
1369
1370#
1371# NameTransferFunction --
1372#
1373#       Creates a transfer function name based on the <style> settings in the
1374#       library run.xml file. This placeholder will be used later to create
1375#       and send the actual transfer function once the data info has been sent
1376#       to us by the render server. [We won't know the volume limits until the
1377#       server parses the 3D data and sends back the limits via ReceiveData.]
1378#
1379itcl::body Rappture::NanovisViewer::NameTransferFunction { dataobj cname } {
1380    array set styles {
1381        -color BCGYR
1382        -levels 6
1383        -markers ""
1384    }
1385    set tag $dataobj-$cname
1386    array set styles [lindex [$dataobj components -style $cname] 0]
1387    if { ![info exists _cname2transferFunction($cname)] } {
1388        # Get the colormap right now, since it doesn't change with marker
1389        # changes.
1390        set cmap [ColorsToColormap $styles(-color)]
1391        set wmap [list 0.0 0.0 1.0 1.0]
1392        set _cname2transferFunction($cname) [list $cmap $wmap]
1393        SendCmd [list transfunc define $cname $cmap $wmap]
1394    }
1395    SendCmd "volume shading transfunc $cname $tag"
1396    if { ![info exists _transferFunctionEditors($cname)] } {
1397        set _transferFunctionEditors($cname) \
1398            [Rappture::TransferFunctionEditor ::\#auto $itk_component(legend) \
1399                 $cname \
1400                 -command [itcl::code $this updateTransferFunctions]]
1401    }
1402    set _dataset2style($tag) $cname
1403    lappend _style2datasets($cname) $tag
1404    return $cname
1405}
1406
1407#
1408# ComputeTransferFunction --
1409#
1410#       Computes and sends the transfer function to the render server.  It's
1411#       assumed that the volume data limits are known and that the global
1412#       transfer-functions slider values have been set up.  Both parts are
1413#       needed to compute the relative value (location) of the marker, and
1414#       the alpha map of the transfer function.
1415#
1416itcl::body Rappture::NanovisViewer::ComputeTransferFunction { cname } {
1417    foreach {cmap wmap} $_cname2transferFunction($cname) break
1418
1419    # We have to parse the style attributes for a volume using this
1420    # transfer-function *once*.  This sets up the initial isomarkers for the
1421    # transfer function.  The user may add/delete markers, so we have to
1422    # maintain a list of markers for each transfer-function.  We use the one
1423    # of the volumes (the first in the list) using the transfer-function as a
1424    # reference.
1425    if { ![info exists _parsedFunction($cname)] } {
1426        array set styles {
1427            -color BCGYR
1428            -levels 6
1429            -markers ""
1430        }
1431        # Accumulate the style from all the datasets using it.
1432        foreach tag [GetDatasetsWithComponent $cname] {
1433            foreach {dataobj cname} [split [lindex $tag 0] -] break
1434            array set styles [lindex [$dataobj components -style $cname] 0]
1435        }
1436        eval $_transferFunctionEditors($cname) limits $_limits($cname)
1437        # Have to defer creation of isomarkers until we have data limits
1438        if { [info exists styles(-markers)] &&
1439             [llength $styles(-markers)] > 0 } {
1440            ParseMarkersOption $cname $styles(-markers)
1441        } else {
1442            ParseLevelsOption $cname $styles(-levels)
1443        }
1444       
1445    }
1446    set wmap [ComputeAlphamap $cname]
1447    set _cname2transferFunction($cname) [list $cmap $wmap]
1448    SendCmd [list transfunc define $cname $cmap $wmap]
1449}
1450
1451itcl::body Rappture::NanovisViewer::AddNewMarker { x y } {
1452    if { ![info exists _transferFunctionEditors($_current)] } {
1453        continue
1454    }
1455    # Add a new marker to the current transfer function
1456    $_transferFunctionEditors($_current) newMarker $x $y normal
1457    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1458}
1459
1460itcl::body Rappture::NanovisViewer::RemoveMarker { x y } {
1461    if { ![info exists _transferFunctionEditors($_current)] } {
1462        continue
1463    }
1464    # Add a new marker to the current transfer function
1465    $_transferFunctionEditors($_current) deleteMarker $x $y
1466}
1467
1468# ----------------------------------------------------------------------
1469# CONFIGURATION OPTION: -plotbackground
1470# ----------------------------------------------------------------------
1471itcl::configbody Rappture::NanovisViewer::plotbackground {
1472    if { [isconnected] } {
1473        set color $itk_option(-plotbackground)
1474        set rgb [Color2RGB $color]
1475        SendCmd "screen bgcolor $rgb"
1476        $itk_component(legend) configure -background $color
1477    }
1478}
1479
1480# ----------------------------------------------------------------------
1481# CONFIGURATION OPTION: -plotforeground
1482# ----------------------------------------------------------------------
1483itcl::configbody Rappture::NanovisViewer::plotforeground {
1484    if { [isconnected] } {
1485        set color $itk_option(-plotforeground)
1486        set rgb [Color2RGB $color]
1487        SendCmd "volume outline color $rgb"
1488        SendCmd "grid axiscolor $rgb"
1489        SendCmd "grid linecolor $rgb"
1490        $itk_component(legend) itemconfigure labels -fill $color
1491        $itk_component(legend) itemconfigure limits -fill $color
1492    }
1493}
1494
1495# ----------------------------------------------------------------------
1496# CONFIGURATION OPTION: -plotoutline
1497# ----------------------------------------------------------------------
1498itcl::configbody Rappture::NanovisViewer::plotoutline {
1499    # Must check if we are connected because this routine is called from the
1500    # class body when the -plotoutline itk_option is defined.  At that point
1501    # the NanovisViewer class constructor hasn't been called, so we can't
1502    # start sending commands to visualization server.
1503    if { [isconnected] } {
1504        if {"" == $itk_option(-plotoutline)} {
1505            SendCmd "volume outline state off"
1506        } else {
1507            SendCmd "volume outline state on"
1508            SendCmd "volume outline color [Color2RGB $itk_option(-plotoutline)]"
1509        }
1510    }
1511}
1512
1513#
1514# The -levels option takes a single value that represents the number
1515# of evenly distributed markers based on the current data range. Each
1516# marker is a relative value from 0.0 to 1.0.
1517#
1518itcl::body Rappture::NanovisViewer::ParseLevelsOption { cname levels } {
1519    set c $itk_component(legend)
1520    set list {}
1521    regsub -all "," $levels " " levels
1522    if {[string is int $levels]} {
1523        for {set i 1} { $i <= $levels } {incr i} {
1524            lappend list [expr {double($i)/($levels+1)}]
1525        }
1526    } else {
1527        foreach x $levels {
1528            lappend list $x
1529        }
1530    }
1531    set _parsedFunction($cname) 1
1532    $_transferFunctionEditors($cname) addMarkers $list
1533    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1534}
1535
1536#
1537# The -markers option takes a list of zero or more values (the values
1538# may be separated either by spaces or commas) that have the following
1539# format:
1540#
1541#   N%  Percent of current total data range.  Converted to
1542#       to a relative value between 0.0 and 1.0.
1543#   N   Absolute value of marker.  If the marker is outside of
1544#       the current range, it will be displayed on the outer
1545#       edge of the legends, but it range it represents will
1546#       not be seen.
1547#
1548itcl::body Rappture::NanovisViewer::ParseMarkersOption { cname markers } {
1549    set c $itk_component(legend)
1550    set list {}
1551    foreach { min max } $_limits($cname) break
1552    regsub -all "," $markers " " markers
1553    foreach marker $markers {
1554        set n [scan $marker "%g%s" value suffix]
1555        if { $n == 2 && $suffix == "%" } {
1556            # $n% : Set relative value (0..1).
1557            lappend list [expr {$value * 0.01}]
1558        } else {
1559            # $n : absolute value, compute relative
1560            lappend list  [expr {(double($value)-$min)/($max-$min)]}
1561        }
1562    }
1563    set _parsedFunction($cname) 1
1564    $_transferFunctionEditors($cname) addMarkers $list
1565    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1566}
1567
1568# ----------------------------------------------------------------------
1569# USAGE: UndateTransferFuncs
1570# ----------------------------------------------------------------------
1571itcl::body Rappture::NanovisViewer::updateTransferFunctions {} {
1572    $_dispatcher event -idle !send_transfunc
1573}
1574
1575itcl::body Rappture::NanovisViewer::limits { cname } {
1576    set _limits(min) 0.0
1577    set _limits(max) 1.0
1578    if { ![info exists _style2datasets($cname)] } {
1579        return [array get _limits]
1580    }
1581    set min ""; set max ""
1582    foreach tag [GetDatasetsWithComponent $cname] {
1583        if { ![info exists _limits($tag)] } {
1584            continue
1585        }
1586        foreach {amin amax} $_limits($tag) break
1587        if { $min == "" || $min > $amin } {
1588            set min $amin
1589        }
1590        if { $max == "" || $max < $amax } {
1591            set max $amax
1592        }
1593    }
1594    if { $min != "" } {
1595        set _limits(min) $min
1596    }
1597    if { $max != "" } {
1598        set _limits(max) $max
1599    }
1600    return [list $_limits(min) $_limits(max)]
1601}
1602
1603
1604itcl::body Rappture::NanovisViewer::BuildViewTab {} {
1605    set fg [option get $itk_component(hull) font Font]
1606    #set bfg [option get $itk_component(hull) boldFont Font]
1607
1608    set inner [$itk_component(main) insert end \
1609        -title "View Settings" \
1610        -icon [Rappture::icon wrench]]
1611    $inner configure -borderwidth 4
1612
1613    set ::Rappture::NanovisViewer::_settings(-isosurfaceshading) 0
1614    checkbutton $inner.isosurface \
1615        -text "Isosurface shading" \
1616        -variable [itcl::scope _settings(-isosurfaceshading)] \
1617        -command [itcl::code $this AdjustSetting -isosurfaceshading] \
1618        -font "Arial 9"
1619
1620    checkbutton $inner.axes \
1621        -text "Axes" \
1622        -variable [itcl::scope _settings(-axesvisible)] \
1623        -command [itcl::code $this AdjustSetting -axesvisible] \
1624        -font "Arial 9"
1625
1626    checkbutton $inner.grid \
1627        -text "Grid" \
1628        -variable [itcl::scope _settings(-gridvisible)] \
1629        -command [itcl::code $this AdjustSetting -gridvisible] \
1630        -font "Arial 9"
1631
1632    checkbutton $inner.outline \
1633        -text "Outline" \
1634        -variable [itcl::scope _settings(-outlinevisible)] \
1635        -command [itcl::code $this AdjustSetting -outlinevisible] \
1636        -font "Arial 9"
1637
1638    checkbutton $inner.legend \
1639        -text "Legend" \
1640        -variable [itcl::scope _settings(-legendvisible)] \
1641        -command [itcl::code $this AdjustSetting -legendvisible] \
1642        -font "Arial 9"
1643
1644    checkbutton $inner.volume \
1645        -text "Volume" \
1646        -variable [itcl::scope _settings(-volume)] \
1647        -command [itcl::code $this AdjustSetting -volume] \
1648        -font "Arial 9"
1649
1650    label $inner.background_l -text "Background" -font "Arial 9"
1651    itk_component add background {
1652        Rappture::Combobox $inner.background -width 10 -editable no
1653    }
1654    $inner.background choices insert end \
1655        "black"              "black"            \
1656        "white"              "white"            \
1657        "grey"               "grey"             
1658
1659    $itk_component(background) value $_settings(-background)
1660    bind $inner.background <<Value>> \
1661        [itcl::code $this AdjustSetting -background]
1662
1663    blt::table $inner \
1664        0,0 $inner.axes  -cspan 2 -anchor w \
1665        1,0 $inner.grid  -cspan 2 -anchor w \
1666        2,0 $inner.outline  -cspan 2 -anchor w \
1667        3,0 $inner.volume  -cspan 2 -anchor w \
1668        4,0 $inner.legend  -cspan 2 -anchor w \
1669        5,0 $inner.background_l       -anchor e -pady 2 \
1670        5,1 $inner.background                   -fill x \
1671
1672    if 0 {
1673    bind $inner <Map> [itcl::code $this GetVolumeInfo $inner]
1674    }
1675    blt::table configure $inner r* -resize none
1676    blt::table configure $inner r6 -resize expand
1677}
1678
1679itcl::body Rappture::NanovisViewer::BuildVolumeTab {} {
1680    set inner [$itk_component(main) insert end \
1681        -title "Volume Settings" \
1682        -icon [Rappture::icon volume-on]]
1683    $inner configure -borderwidth 4
1684
1685    set font [option get $itk_component(hull) font Font]
1686    set fg [option get $itk_component(hull) font Font]
1687    #set bfg [option get $itk_component(hull) boldFont Font]
1688
1689    checkbutton $inner.vol -text "Show volume" -font $fg \
1690        -variable [itcl::scope _settings(-volumevisible)] \
1691        -command [itcl::code $this AdjustSetting -volumevisible]
1692    label $inner.shading -text "Shading:" -font $fg
1693
1694    checkbutton $inner.light2side -text "Two-sided lighting" -font $fg \
1695        -variable [itcl::scope _settings(-light2side)] \
1696        -command [itcl::code $this AdjustSetting -light2side]
1697
1698    label $inner.dim -text "Glow" -font $fg
1699    ::scale $inner.light -from 0 -to 100 -orient horizontal \
1700        -variable [itcl::scope _settings(-light)] \
1701        -width 10 \
1702        -showvalue off -command [itcl::code $this AdjustSetting -light]
1703    label $inner.bright -text "Surface" -font $fg
1704
1705    # Opacity
1706    label $inner.fog -text "Clear" -font $fg
1707    ::scale $inner.transp -from 0 -to 100 -orient horizontal \
1708        -variable [itcl::scope _widget(-volumeopacity)] \
1709        -width 10 \
1710        -showvalue off -command [itcl::code $this AdjustSetting -volumeopacity]
1711    label $inner.plastic -text "Opaque" -font $fg
1712
1713    # Tooth thickness
1714    label $inner.thin -text "Thin" -font $fg
1715    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
1716        -variable [itcl::scope _settings(-thickness)] \
1717        -width 10 \
1718        -showvalue off -command [itcl::code $this AdjustSetting -thickness]
1719    label $inner.thick -text "Thick" -font $fg
1720
1721    # Colormap
1722    label $inner.colormap_l -text "Colormap" -font "Arial 9"
1723    itk_component add colormap {
1724        Rappture::Combobox $inner.colormap -width 10 -editable no
1725    }
1726
1727    $inner.colormap choices insert end [GetColormapList -includeDefault -includeNone]
1728    bind $inner.colormap <<Value>> \
1729        [itcl::code $this AdjustSetting -colormap]
1730    $itk_component(colormap) value "default"
1731    set _settings(-colormap) "default"
1732
1733    # Component
1734    label $inner.volcomponents_l -text "Component" -font $font
1735    itk_component add volcomponents {
1736        Rappture::Combobox $inner.volcomponents -editable no
1737    }
1738    bind $inner.volcomponents <<Value>> \
1739        [itcl::code $this AdjustSetting -current]
1740
1741    blt::table $inner \
1742        0,0 $inner.volcomponents_l -anchor e -cspan 2 \
1743        0,2 $inner.volcomponents             -cspan 3 -fill x \
1744        1,0 $inner.shading -cspan 4 -anchor w -pady {10 2} \
1745        2,0 $inner.light2side -cspan 4 -anchor w -pady 2 \
1746        3,0 $inner.dim -anchor e -pady 2 \
1747        3,1 $inner.light -cspan 2 -pady 2 -fill x \
1748        3,3 $inner.bright -anchor w -pady 2 \
1749        4,0 $inner.fog -anchor e -pady 2 \
1750        4,1 $inner.transp -cspan 2 -pady 2 -fill x \
1751        4,3 $inner.plastic -anchor w -pady 2 \
1752        5,0 $inner.thin -anchor e -pady 2 \
1753        5,1 $inner.thickness -cspan 2 -pady 2 -fill x\
1754        5,3 $inner.thick -anchor w -pady 2
1755
1756    blt::table configure $inner c0 c1 c3 r* -resize none
1757    blt::table configure $inner r6 -resize expand
1758}
1759
1760itcl::body Rappture::NanovisViewer::BuildCutplanesTab {} {
1761    set inner [$itk_component(main) insert end \
1762        -title "Cutplane Settings" \
1763        -icon [Rappture::icon cutbutton]]
1764    $inner configure -borderwidth 4
1765
1766    checkbutton $inner.visible \
1767        -text "Show Cutplanes" \
1768        -variable [itcl::scope _settings(-cutplanesvisible)] \
1769        -command [itcl::code $this AdjustSetting -cutplanesvisible] \
1770        -font "Arial 9"
1771   
1772    # X-value slicer...
1773    itk_component add xCutButton {
1774        Rappture::PushButton $inner.xbutton \
1775            -onimage [Rappture::icon x-cutplane] \
1776            -offimage [Rappture::icon x-cutplane] \
1777            -command [itcl::code $this AdjustSetting -xcutplanevisible] \
1778            -variable [itcl::scope _settings(-xcutplanevisible)]
1779    }
1780    Rappture::Tooltip::for $itk_component(xCutButton) \
1781        "Toggle the X cut plane on/off"
1782    $itk_component(xCutButton) select
1783
1784    itk_component add xCutScale {
1785        ::scale $inner.xval -from 100 -to 0 \
1786            -width 10 -orient vertical -showvalue off \
1787            -borderwidth 1 -highlightthickness 0 \
1788            -command [itcl::code $this Slice move x] \
1789            -variable [itcl::scope _settings(-xcutplaneposition)]
1790    } {
1791        usual
1792        ignore -borderwidth -highlightthickness
1793    }
1794    # Set the default cutplane value before disabling the scale.
1795    $itk_component(xCutScale) set 50
1796    $itk_component(xCutScale) configure -state disabled
1797    Rappture::Tooltip::for $itk_component(xCutScale) \
1798        "@[itcl::code $this SlicerTip x]"
1799
1800    # Y-value slicer...
1801    itk_component add yCutButton {
1802        Rappture::PushButton $inner.ybutton \
1803            -onimage [Rappture::icon y-cutplane] \
1804            -offimage [Rappture::icon y-cutplane] \
1805            -command [itcl::code $this AdjustSetting -ycutplanevisible] \
1806            -variable [itcl::scope _settings(-ycutplanevisible)]
1807    }
1808    Rappture::Tooltip::for $itk_component(yCutButton) \
1809        "Toggle the Y cut plane on/off"
1810    $itk_component(yCutButton) select
1811
1812    itk_component add yCutScale {
1813        ::scale $inner.yval -from 100 -to 0 \
1814            -width 10 -orient vertical -showvalue off \
1815            -borderwidth 1 -highlightthickness 0 \
1816            -command [itcl::code $this Slice move y] \
1817            -variable [itcl::scope _settings(-ycutplaneposition)]
1818    } {
1819        usual
1820        ignore -borderwidth -highlightthickness
1821    }
1822    Rappture::Tooltip::for $itk_component(yCutScale) \
1823        "@[itcl::code $this SlicerTip y]"
1824    # Set the default cutplane value before disabling the scale.
1825    $itk_component(yCutScale) set 50
1826    $itk_component(yCutScale) configure -state disabled
1827
1828    # Z-value slicer...
1829    itk_component add zCutButton {
1830        Rappture::PushButton $inner.zbutton \
1831            -onimage [Rappture::icon z-cutplane] \
1832            -offimage [Rappture::icon z-cutplane] \
1833            -command [itcl::code $this AdjustSetting -zcutplanevisible] \
1834            -variable [itcl::scope _settings(-zcutplanevisible)]
1835    }
1836    Rappture::Tooltip::for $itk_component(zCutButton) \
1837        "Toggle the Z cut plane on/off"
1838    $itk_component(zCutButton) select
1839
1840    itk_component add zCutScale {
1841        ::scale $inner.zval -from 100 -to 0 \
1842            -width 10 -orient vertical -showvalue off \
1843            -borderwidth 1 -highlightthickness 0 \
1844            -command [itcl::code $this Slice move z] \
1845            -variable [itcl::scope _settings(-zcutplaneposition)]
1846    } {
1847        usual
1848        ignore -borderwidth -highlightthickness
1849    }
1850    $itk_component(zCutScale) set 50
1851    $itk_component(zCutScale) configure -state disabled
1852    Rappture::Tooltip::for $itk_component(zCutScale) \
1853        "@[itcl::code $this SlicerTip z]"
1854
1855    blt::table $inner \
1856        1,1 $itk_component(xCutButton) \
1857        1,2 $itk_component(yCutButton) \
1858        1,3 $itk_component(zCutButton) \
1859        0,1 $itk_component(xCutScale) \
1860        0,2 $itk_component(yCutScale) \
1861        0,3 $itk_component(zCutScale)
1862
1863    blt::table configure $inner r0 r1 c* -resize none
1864    blt::table configure $inner r2 c4 -resize expand
1865    blt::table configure $inner c0 -width 2
1866    blt::table configure $inner c1 c2 c3 -padx 2
1867}
1868
1869itcl::body Rappture::NanovisViewer::BuildCameraTab {} {
1870    set inner [$itk_component(main) insert end \
1871        -title "Camera Settings" \
1872        -icon [Rappture::icon camera]]
1873    $inner configure -borderwidth 4
1874
1875    label $inner.view_l -text "view" -font "Arial 9"
1876    set f [frame $inner.view]
1877    foreach side { front back left right top bottom } {
1878        button $f.$side  -image [Rappture::icon view$side] \
1879            -command [itcl::code $this SetOrientation $side]
1880        Rappture::Tooltip::for $f.$side "Change the view to $side"
1881        pack $f.$side -side left
1882    }
1883
1884    blt::table $inner \
1885        0,0 $inner.view_l -anchor e -pady 2 \
1886        0,1 $inner.view -anchor w -pady 2
1887
1888    set row 1
1889    set labels { qw qx qy qz xpan ypan zoom }
1890    foreach tag $labels {
1891        label $inner.${tag}label -text $tag -font "Arial 9"
1892        entry $inner.${tag} -font "Arial 9"  -bg white \
1893            -textvariable [itcl::scope _settings(-$tag)]
1894        bind $inner.${tag} <Return> \
1895            [itcl::code $this camera set -${tag}]
1896        bind $inner.${tag} <KP_Enter> \
1897            [itcl::code $this camera set -${tag}]
1898        blt::table $inner \
1899            $row,0 $inner.${tag}label -anchor e -pady 2 \
1900            $row,1 $inner.${tag} -anchor w -pady 2
1901        blt::table configure $inner r$row -resize none
1902        incr row
1903    }
1904
1905    blt::table configure $inner c* r* -resize none
1906    blt::table configure $inner c2 -resize expand
1907    blt::table configure $inner r$row -resize expand
1908}
1909
1910# ----------------------------------------------------------------------
1911# USAGE: Slice move x|y|z <newval>
1912#
1913# Called automatically when the user drags the slider to move the
1914# cut plane that slices 3D data.  Gets the current value from the
1915# slider and moves the cut plane to the appropriate point in the
1916# data set.
1917# ----------------------------------------------------------------------
1918itcl::body Rappture::NanovisViewer::Slice {option args} {
1919    switch -- $option {
1920        move {
1921            if {[llength $args] != 2} {
1922                error "wrong # args: should be \"Slice move x|y|z newval\""
1923            }
1924            set axis [lindex $args 0]
1925            set newval [lindex $args 1]
1926
1927            set newpos [expr {0.01*$newval}]
1928            set datasets [CurrentDatasets -cutplanes]
1929            set tag [lindex $datasets 0]
1930            SendCmd "cutplane position $newpos $axis $tag"
1931        }
1932        default {
1933            error "bad option \"$option\": should be axis, move, or volume"
1934        }
1935    }
1936}
1937
1938# ----------------------------------------------------------------------
1939# USAGE: SlicerTip <axis>
1940#
1941# Used internally to generate a tooltip for the x/y/z slicer controls.
1942# Returns a message that includes the current slicer value.
1943# ----------------------------------------------------------------------
1944itcl::body Rappture::NanovisViewer::SlicerTip {axis} {
1945    set val [$itk_component(${axis}CutScale) get]
1946    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
1947}
1948
1949
1950itcl::body Rappture::NanovisViewer::DoResize {} {
1951    $_arcball resize $_width $_height
1952    SendCmd "screen size $_width $_height"
1953    set _resizePending 0
1954}
1955
1956itcl::body Rappture::NanovisViewer::EventuallyResize { w h } {
1957    set _width $w
1958    set _height $h
1959    $_arcball resize $w $h
1960    if { !$_resizePending } {
1961        $_dispatcher event -idle !resize
1962        set _resizePending 1
1963    }
1964}
1965
1966itcl::body Rappture::NanovisViewer::EventuallyRedrawLegend {} {
1967    if { !$_resizeLegendPending } {
1968        $_dispatcher event -idle !legend
1969        set _resizeLegendPending 1
1970    }
1971}
1972
1973#  camera --
1974#
1975itcl::body Rappture::NanovisViewer::camera {option args} {
1976    switch -- $option {
1977        "show" {
1978            puts [array get _view]
1979        }
1980        "set" {
1981            set what [lindex $args 0]
1982            set x $_settings($what)
1983            set code [catch { string is double $x } result]
1984            if { $code != 0 || !$result } {
1985                set _settings($what) $_view($what)
1986                return
1987            }
1988            switch -- $what {
1989                "-xpan" - "-ypan" {
1990                    set _view($what) $_settings($what)
1991                    PanCamera
1992                }
1993                "-qx" - "-qy" - "-qz" - "-qw" {
1994                    set _view($what) $_settings($what)
1995                    set q [ViewToQuaternion]
1996                    $_arcball quaternion $q
1997                    SendCmd "camera orient $q"
1998                }
1999                "-zoom" {
2000                    set _view($what) $_settings($what)
2001                    SendCmd "camera zoom $_view($what)"
2002                }
2003            }
2004        }
2005    }
2006}
2007
2008itcl::body Rappture::NanovisViewer::GetVolumeInfo { w } {
2009    set flowobj ""
2010    foreach key [array names _obj2flow] {
2011        set flowobj $_obj2flow($key)
2012        break
2013    }
2014    if { $flowobj == "" } {
2015        return
2016    }
2017    if { [winfo exists $w.frame] } {
2018        destroy $w.frame
2019    }
2020    set inner [frame $w.frame]
2021    blt::table $w \
2022        5,0 $inner -fill both -cspan 2 -anchor nw
2023    array set hints [$dataobj hints]
2024
2025    label $inner.volumes -text "Volumes" -font "Arial 9 bold"
2026    blt::table $inner \
2027        1,0 $inner.volumes  -anchor w \
2028    blt::table configure $inner c0 c1 -resize none
2029    blt::table configure $inner c2 -resize expand
2030
2031    set row 3
2032    set volumes [get]
2033    if { [llength $volumes] > 0 } {
2034        blt::table $inner $row,0 $inner.volumes  -anchor w
2035        incr row
2036    }
2037    foreach vol $volumes {
2038        array unset info
2039        array set info $vol
2040        set name $info(name)
2041        if { ![info exists _settings(-volumevisible-$name)] } {
2042            set _settings(-volumevisible-$name) $info(hide)
2043        }
2044        checkbutton $inner.vol$row -text $info(label) \
2045            -variable [itcl::scope _settings(-volumevisible-$name)] \
2046            -onvalue 0 -offvalue 1 \
2047            -command [itcl::code $this ToggleVolume $key $name] \
2048            -font "Arial 9"
2049        Rappture::Tooltip::for $inner.vol$row $info(description)
2050        blt::table $inner $row,0 $inner.vol$row -anchor w
2051        if { !$_settings(-volume-$name) } {
2052            $inner.vol$row select
2053        }
2054        incr row
2055    }
2056    blt::table configure $inner r* -resize none
2057    blt::table configure $inner r$row -resize expand
2058    blt::table configure $inner c3 -resize expand
2059    event generate [winfo parent [winfo parent $w]] <Configure>
2060}
2061
2062itcl::body Rappture::NanovisViewer::ToggleVolume { tag name } {
2063    set bool $_settings(-volumevisible-$name)
2064    SendCmd "volume state $bool $name"
2065}
2066
2067itcl::body Rappture::NanovisViewer::SetOrientation { side } {
2068    array set positions {
2069        front "1 0 0 0"
2070        back  "0 0 1 0"
2071        left  "0.707107 0 -0.707107 0"
2072        right "0.707107 0 0.707107 0"
2073        top   "0.707107 -0.707107 0 0"
2074        bottom "0.707107 0.707107 0 0"
2075    }
2076    foreach name { -qw -qx -qy -qz } value $positions($side) {
2077        set _view($name) $value
2078    }
2079    set q [ViewToQuaternion]
2080    $_arcball quaternion $q
2081    SendCmd "camera orient $q"
2082    SendCmd "camera reset"
2083    set _view(-xpan) 0
2084    set _view(-ypan) 0
2085    set _view(-zoom) 1.0
2086    set _settings(-xpan) $_view(-xpan)
2087    set _settings(-ypan) $_view(-ypan)
2088    set _settings(-zoom) $_view(-zoom)
2089}
2090
2091
2092#
2093# InitComponentSettings --
2094#
2095#    Initializes the volume settings for a specific component. This should
2096#    match what's used as global settings above. This is called the first
2097#    time we try to switch to a given component in SwitchComponent below.
2098#
2099itcl::body Rappture::NanovisViewer::InitComponentSettings { cname } {
2100    foreach {key value} {
2101        -colormap          "default"
2102        -light             40
2103        -light2side        1
2104        -thickness         350
2105        -volumeopacity     0.5
2106        -volumevisible     1
2107    } {
2108        if { ![info exists _settings($cname${key})] } {
2109            # Don't override existing component settings
2110            set _settings($cname${key}) $value
2111        }
2112    }
2113}
2114
2115#
2116# SwitchComponent --
2117#
2118#    This is called when the current component is changed by the dropdown
2119#    menu in the volume tab.  It synchronizes the global volume settings
2120#    with the settings of the new current component.
2121#
2122itcl::body Rappture::NanovisViewer::SwitchComponent { cname } {
2123    if { ![info exists _settings($cname-light)] } {
2124        InitComponentSettings $cname
2125    }
2126    if { $_settings(-colormap) != $_settings($cname-colormap) } {
2127        set _settings(-colormap)         $_settings($cname-colormap)
2128        EventuallyRedrawLegend
2129    }
2130    # _settings variables change widgets, except for colormap
2131    set _settings(-light)            $_settings($cname-light)
2132    set _settings(-light2side)       $_settings($cname-light2side)
2133    set _settings(-volumeopacity)    $_settings($cname-volumeopacity)
2134    set _settings(-thickness)        $_settings($cname-thickness)
2135    set _settings(-volumevisible)    $_settings($cname-volumevisible)
2136    $itk_component(colormap) value   $_settings($cname-colormap)
2137
2138    set _widget(-volumeopacity) [expr $_settings(-volumeopacity) * 100.0]
2139
2140    set _current $cname;                # Reset the current component
2141}
2142
2143#
2144# BuildVolumeComponents --
2145#
2146#    This is called from the "scale" method which is called when a new
2147#    dataset is added or deleted.  It repopulates the dropdown menu of
2148#    volume component names.  It sets the current component to the first
2149#    component in the list (of components found).  Finally, if there is
2150#    only one component, don't display the label or the combobox in the
2151#    volume settings tab.
2152#
2153itcl::body Rappture::NanovisViewer::BuildVolumeComponents {} {
2154    $itk_component(volcomponents) choices delete 0 end
2155    foreach name $_componentsList {
2156        $itk_component(volcomponents) choices insert end $name $name
2157    }
2158    set _current [lindex $_componentsList 0]
2159    $itk_component(volcomponents) value $_current
2160    set parent [winfo parent $itk_component(volcomponents)]
2161    if { [llength $_componentsList] <= 1 } {
2162        # Unpack the components label and dropdown if there's only one
2163        # component.
2164        blt::table forget $parent.volcomponents_l $parent.volcomponents
2165    } else {
2166        # Pack the components label and dropdown into the table there's
2167        # more than one component to select.
2168        blt::table $parent \
2169            0,0 $parent.volcomponents_l -anchor e -cspan 2 \
2170            0,2 $parent.volcomponents -cspan 3 -fill x
2171    }
2172}
2173
2174#
2175# GetDatasetsWithComponents --
2176#
2177#     Returns a list of all the datasets (known by the combination of their
2178#     data object and component name) that match the given component name.
2179#     For example, this is used where we want to change the settings of
2180#     volumes that have the current component.
2181#
2182itcl::body Rappture::NanovisViewer::GetDatasetsWithComponent { cname } {
2183    if { ![info exists _volcomponents($cname)] } {
2184        return ""
2185    }
2186    set list ""
2187    foreach tag $_volcomponents($cname) {
2188        if { ![info exists _serverDatasets($tag)] } {
2189            continue
2190        }
2191        lappend list $tag
2192    }
2193    return $list
2194}
2195
2196#
2197# HideAllMarkers --
2198#
2199#   Hide all the markers in all the transfer functions.  Can't simply
2200#   delete and recreate markers from the <style> since the user may have
2201#   create, deleted, or moved markers.
2202#
2203itcl::body Rappture::NanovisViewer::HideAllMarkers {} {
2204    foreach cname [array names _transferFunctionEditors] {
2205        $_transferFunctionEditors($cname) hideMarkers
2206    }
2207}
2208
2209itcl::body Rappture::NanovisViewer::GetColormap { cname color } {
2210    if { $color == "default" } {
2211        return $_cname2defaultcolormap($cname)
2212    }
2213    return [ColorsToColormap $color]
2214}
2215
2216itcl::body Rappture::NanovisViewer::GetAlphamap { cname name } {
2217    if { $name == "default" } {
2218        return $_cname2defaultalphamap($cname)
2219    }
2220    return [NameToAlphamap $name]
2221}
2222
2223itcl::body Rappture::NanovisViewer::ResetColormap { cname color } {
2224    # Get the current transfer function
2225    if { ![info exists _cname2transferFunction($cname)] } {
2226        return
2227    }
2228    foreach { cmap wmap } $_cname2transferFunction($cname) break
2229    set cmap [GetColormap $cname $color]
2230    set _cname2transferFunction($cname) [list $cmap $wmap]
2231    SendCmd [list transfunc define $cname $cmap $wmap]
2232    EventuallyRedrawLegend
2233}
2234
2235itcl::body Rappture::NanovisViewer::ComputeAlphamap { cname } {
2236    if { ![info exists _transferFunctionEditors($cname)] } {
2237        return [list 0.0 0.0 1.0 1.0]
2238    }
2239    if { ![info exists _settings($cname-light)] } {
2240        InitComponentSettings $cname
2241    }
2242
2243    set isovalues [$_transferFunctionEditors($cname) values]
2244
2245    # Currently using volume shading opacity to scale opacity in
2246    # the volume shader. The transfer function always sets full
2247    # opacity
2248    set max 1.0
2249
2250    # Use the component-wise thickness setting from the slider
2251    # settings widget
2252    # Scale values between 0.00001 and 0.01000
2253    set delta [expr {double($_settings($cname-thickness)) * 0.0001}]
2254   
2255    set first [lindex $isovalues 0]
2256    set last [lindex $isovalues end]
2257    set wmap ""
2258    if { $first == "" || $first != 0.0 } {
2259        lappend wmap 0.0 0.0
2260    }
2261    foreach x $isovalues {
2262        set x1 [expr {$x-$delta-0.00001}]
2263        set x2 [expr {$x-$delta}]
2264        set x3 [expr {$x+$delta}]
2265        set x4 [expr {$x+$delta+0.00001}]
2266        if { $x1 < 0.0 } {
2267            set x1 0.0
2268        } elseif { $x1 > 1.0 } {
2269            set x1 1.0
2270        }
2271        if { $x2 < 0.0 } {
2272            set x2 0.0
2273        } elseif { $x2 > 1.0 } {
2274            set x2 1.0
2275        }
2276        if { $x3 < 0.0 } {
2277            set x3 0.0
2278        } elseif { $x3 > 1.0 } {
2279            set x3 1.0
2280        }
2281        if { $x4 < 0.0 } {
2282            set x4 0.0
2283        } elseif { $x4 > 1.0 } {
2284            set x4 1.0
2285        }
2286        # add spikes in the middle
2287        lappend wmap $x1 0.0
2288        lappend wmap $x2 $max
2289        lappend wmap $x3 $max
2290        lappend wmap $x4 0.0
2291    }
2292    if { $last == "" || $last != 1.0 } {
2293        lappend wmap 1.0 0.0
2294    }
2295    return $wmap
2296}
2297
2298
2299itcl::body Rappture::NanovisViewer::NameToAlphamap { name } {
2300    switch -- $name {
2301        "ramp-up" {
2302            set wmap {
2303                0.0 0.0
2304                1.0 1.0
2305            }
2306        }
2307        "ramp-down" {
2308            set wmap {
2309                0.0 1.0
2310                1.0 0.0
2311            }
2312        }
2313        "vee" {
2314            set wmap {
2315                0.0 1.0
2316                0.5 0.0
2317                1.0 1.0
2318            }
2319        }
2320        "tent-1" {
2321            set wmap {
2322                0.0 0.0
2323                0.5 1.0
2324                1.0 0.0
2325            }
2326        }
2327        "tent-2" {
2328            set wmap {
2329                0.0 0.0
2330                0.25 1.0
2331                0.5 0.0
2332                0.75 1.0
2333                1.0 0.0
2334            }
2335        }
2336        "tent-3" {
2337            set wmap {
2338                0.0 0.0
2339                0.16666 1.0
2340                0.33333 0.0
2341                0.5     1.0
2342                0.66666 0.0
2343                0.83333 1.0
2344                1.0 0.0
2345            }
2346        }
2347        "tent-4" {
2348            set wmap {
2349                0.0     0.0
2350                0.125   1.0
2351                0.25    0.0
2352                0.375   1.0
2353                0.5     0.0       
2354                0.625   1.0
2355                0.75    0.0
2356                0.875   1.0
2357                1.0     0.0
2358            }
2359        }
2360        "sinusoid-1" {
2361            set wmap {
2362                0.0                     0.000 0.600 0.800
2363                0.14285714285714285     0.400 0.900 1.000
2364                0.2857142857142857      0.600 1.000 1.000
2365                0.42857142857142855     0.800 1.000 1.000
2366                0.5714285714285714      0.900 0.900 0.900
2367                0.7142857142857143      0.600 0.600 0.600
2368                0.8571428571428571      0.400 0.400 0.400
2369                1.0                     0.200 0.200 0.200
2370            }
2371        }
2372        "sinusoid-2" {
2373            set wmap {
2374                0.0                     0.900 1.000 1.000
2375                0.1111111111111111      0.800 0.983 1.000
2376                0.2222222222222222      0.700 0.950 1.000
2377                0.3333333333333333      0.600 0.900 1.000
2378                0.4444444444444444      0.500 0.833 1.000
2379                0.5555555555555556      0.400 0.750 1.000
2380                0.6666666666666666      0.300 0.650 1.000
2381                0.7777777777777778      0.200 0.533 1.000
2382                0.8888888888888888      0.100 0.400 1.000
2383                1.0                     0.000 0.250 1.000
2384            }
2385        }
2386        "sinusoid-6" {
2387            set wmap {
2388                0.0                             0.200   0.100   0.000
2389                0.09090909090909091             0.400   0.187   0.000
2390                0.18181818181818182             0.600   0.379   0.210
2391                0.2727272727272727              0.800   0.608   0.480
2392                0.36363636363636365             0.850   0.688   0.595
2393                0.45454545454545453             0.950   0.855   0.808
2394                0.5454545454545454              0.800   0.993   1.000
2395                0.6363636363636364              0.600   0.973   1.000
2396                0.7272727272727273              0.400   0.940   1.000
2397                0.8181818181818182              0.200   0.893   1.000
2398                0.9090909090909091              0.000   0.667   0.800
2399                1.0                             0.000   0.480   0.600
2400            }
2401        }
2402        "sinusoid-10" {
2403            set wmap {
2404                0.0                             0.000   0.480   0.600
2405                0.09090909090909091             0.000   0.667   0.800
2406                0.18181818181818182             0.200   0.893   1.000
2407                0.2727272727272727              0.400   0.940   1.000
2408                0.36363636363636365             0.600   0.973   1.000
2409                0.45454545454545453             0.800   0.993   1.000
2410                0.5454545454545454              0.950   0.855   0.808
2411                0.6363636363636364              0.850   0.688   0.595
2412                0.7272727272727273              0.800   0.608   0.480
2413                0.8181818181818182              0.600   0.379   0.210
2414                0.9090909090909091              0.400   0.187   0.000
2415                1.0                             0.200   0.100   0.000
2416            }
2417        }
2418        "step-2" {
2419            set wmap {
2420                0.0                             0.000   0.167   1.000
2421                0.09090909090909091             0.100   0.400   1.000
2422                0.18181818181818182             0.200   0.600   1.000
2423                0.2727272727272727              0.400   0.800   1.000
2424                0.36363636363636365             0.600   0.933   1.000
2425                0.45454545454545453             0.800   1.000   1.000
2426                0.5454545454545454              1.000   1.000   0.800
2427                0.6363636363636364              1.000   0.933   0.600
2428                0.7272727272727273              1.000   0.800   0.400
2429                0.8181818181818182              1.000   0.600   0.200
2430                0.9090909090909091              1.000   0.400   0.100
2431                1.0                             1.000   0.167   0.000
2432            }
2433        }
2434        "step-5" {
2435            set wmap {
2436                0.0                             1.000   0.167   0.000
2437                0.09090909090909091             1.000   0.400   0.100
2438                0.18181818181818182             1.000   0.600   0.200
2439                0.2727272727272727              1.000   0.800   0.400
2440                0.36363636363636365             1.000   0.933   0.600
2441                0.45454545454545453             1.000   1.000   0.800
2442                0.5454545454545454              0.800   1.000   1.000
2443                0.6363636363636364              0.600   0.933   1.000
2444                0.7272727272727273              0.400   0.800   1.000
2445                0.8181818181818182              0.200   0.600   1.000
2446                0.9090909090909091              0.100   0.400   1.000
2447                1.0                             0.000   0.167   1.000
2448            }
2449        }
2450        "step-12" {
2451            set wmap {
2452                "#EE82EE"
2453                "#4B0082"
2454                "blue"
2455                "#008000"
2456                "yellow"
2457                "#FFA500"
2458                "red"
2459            }
2460        }
2461        default {
2462        }
2463    }
2464    return ""
2465}
2466
2467
2468itcl::body Rappture::NanovisViewer::SetObjectStyle { dataobj cname } {
2469    array set styles {
2470        -opacity  0.6
2471    }
2472    array set styles [lindex [$dataobj components -style $cname] 0]
2473    set _settings($cname-volumeopacity) $styles(-opacity)
2474    NameTransferFunction $dataobj $cname
2475}
2476
2477
2478
2479
Note: See TracBrowser for help on using the repository browser.