source: branches/uq/gui/scripts/nanovisviewer.tcl @ 4797

Last change on this file since 4797 was 4797, checked in by gah, 6 years ago

sync with 1.4-pre

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