source: trunk/gui/scripts/nanovisviewer.tcl @ 5562

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

Add title to flowvis legend, fix legend text tags for settings text color

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