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

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

Roll back widget/settings partial refactor in nanovis viewer. We can do a more
complete refactor in the trunk.

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