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

Last change on this file since 5293 was 5293, checked in by ldelgass, 9 years ago

merge r5292 from trunk

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