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

Last change on this file since 5095 was 5095, checked in by ldelgass, 8 years ago

merge r5090:r5094 from trunk

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