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

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

Fix syntax error with bracket/brace nesting in ParseMarkersOption?

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