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

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

fix error msg

File size: 83.5 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: nanovisviewer - 3D volume rendering
4#
5#  This widget performs volume rendering on 3D scalar/vector datasets.
6#  It connects to the Nanovis server running on a rendering farm,
7#  transmits data, and displays the results.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require BLT
17package require Img
18
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 "limits text title"
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 labels -fill $color
1526        $itk_component(legend) itemconfigure limits -fill $color
1527    }
1528}
1529
1530# ----------------------------------------------------------------------
1531# CONFIGURATION OPTION: -plotoutline
1532# ----------------------------------------------------------------------
1533itcl::configbody Rappture::NanovisViewer::plotoutline {
1534    # Must check if we are connected because this routine is called from the
1535    # class body when the -plotoutline itk_option is defined.  At that point
1536    # the NanovisViewer class constructor hasn't been called, so we can't
1537    # start sending commands to visualization server.
1538    if { [isconnected] } {
1539        if {"" == $itk_option(-plotoutline)} {
1540            SendCmd "volume outline state off"
1541        } else {
1542            SendCmd "volume outline state on"
1543            SendCmd "volume outline color [Color2RGB $itk_option(-plotoutline)]"
1544        }
1545    }
1546}
1547
1548#
1549# The -levels option takes a single value that represents the number
1550# of evenly distributed markers based on the current data range. Each
1551# marker is a relative value from 0.0 to 1.0.
1552#
1553itcl::body Rappture::NanovisViewer::ParseLevelsOption { cname levels } {
1554    set c $itk_component(legend)
1555    set list {}
1556    regsub -all "," $levels " " levels
1557    if {[string is int $levels]} {
1558        for {set i 1} { $i <= $levels } {incr i} {
1559            lappend list [expr {double($i)/($levels+1)}]
1560        }
1561    } else {
1562        foreach x $levels {
1563            lappend list $x
1564        }
1565    }
1566    set _parsedFunction($cname) 1
1567    $_transferFunctionEditors($cname) addMarkers $list
1568    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1569}
1570
1571#
1572# The -markers option takes a list of zero or more values (the values
1573# may be separated either by spaces or commas) that have the following
1574# format:
1575#
1576#   N%  Percent of current total data range.  Converted to
1577#       to a relative value between 0.0 and 1.0.
1578#   N   Absolute value of marker.  If the marker is outside of
1579#       the current range, it will be displayed on the outer
1580#       edge of the legends, but it range it represents will
1581#       not be seen.
1582#
1583itcl::body Rappture::NanovisViewer::ParseMarkersOption { cname markers } {
1584    set c $itk_component(legend)
1585    set list {}
1586    foreach { min max } $_limits($cname) break
1587    regsub -all "," $markers " " markers
1588    foreach marker $markers {
1589        set n [scan $marker "%g%s" value suffix]
1590        if { $n == 2 && $suffix == "%" } {
1591            # $n% : Set relative value (0..1).
1592            lappend list [expr {$value * 0.01}]
1593        } else {
1594            # $n : absolute value, compute relative
1595            lappend list  [expr {(double($value)-$min)/($max-$min)]}
1596        }
1597    }
1598    set _parsedFunction($cname) 1
1599    $_transferFunctionEditors($cname) addMarkers $list
1600    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
1601}
1602
1603itcl::body Rappture::NanovisViewer::updateTransferFunctions {} {
1604    $_dispatcher event -idle !send_transfunc
1605}
1606
1607itcl::body Rappture::NanovisViewer::BuildViewTab {} {
1608    set fg [option get $itk_component(hull) font Font]
1609    #set bfg [option get $itk_component(hull) boldFont Font]
1610
1611    set inner [$itk_component(main) insert end \
1612        -title "View Settings" \
1613        -icon [Rappture::icon wrench]]
1614    $inner configure -borderwidth 4
1615
1616    checkbutton $inner.axes \
1617        -text "Axes" \
1618        -variable [itcl::scope _settings(-axesvisible)] \
1619        -command [itcl::code $this AdjustSetting -axesvisible] \
1620        -font "Arial 9"
1621
1622    checkbutton $inner.grid \
1623        -text "Grid" \
1624        -variable [itcl::scope _settings(-gridvisible)] \
1625        -command [itcl::code $this AdjustSetting -gridvisible] \
1626        -font "Arial 9"
1627
1628    checkbutton $inner.outline \
1629        -text "Outline" \
1630        -variable [itcl::scope _settings(-outlinevisible)] \
1631        -command [itcl::code $this AdjustSetting -outlinevisible] \
1632        -font "Arial 9"
1633
1634    checkbutton $inner.legend \
1635        -text "Legend" \
1636        -variable [itcl::scope _settings(-legendvisible)] \
1637        -command [itcl::code $this AdjustSetting -legendvisible] \
1638        -font "Arial 9"
1639
1640    checkbutton $inner.volume \
1641        -text "Volume" \
1642        -variable [itcl::scope _settings(-volume)] \
1643        -command [itcl::code $this AdjustSetting -volume] \
1644        -font "Arial 9"
1645
1646    label $inner.background_l -text "Background" -font "Arial 9"
1647    itk_component add background {
1648        Rappture::Combobox $inner.background -width 10 -editable no
1649    }
1650    $inner.background choices insert end \
1651        "black" "black" \
1652        "white" "white" \
1653        "grey"  "grey"
1654
1655    $itk_component(background) value $_settings(-background)
1656    bind $inner.background <<Value>> \
1657        [itcl::code $this AdjustSetting -background]
1658
1659    blt::table $inner \
1660        0,0 $inner.axes -cspan 2 -anchor w \
1661        1,0 $inner.grid -cspan 2 -anchor w \
1662        2,0 $inner.outline -cspan 2 -anchor w \
1663        3,0 $inner.volume -cspan 2 -anchor w \
1664        4,0 $inner.legend -cspan 2 -anchor w \
1665        5,0 $inner.background_l -anchor e -pady 2 \
1666        5,1 $inner.background -fill x
1667
1668    blt::table configure $inner r* -resize none
1669    blt::table configure $inner r6 -resize expand
1670}
1671
1672itcl::body Rappture::NanovisViewer::BuildVolumeTab {} {
1673    set inner [$itk_component(main) insert end \
1674        -title "Volume Settings" \
1675        -icon [Rappture::icon volume-on]]
1676    $inner configure -borderwidth 4
1677
1678    set fg [option get $itk_component(hull) font Font]
1679    #set bfg [option get $itk_component(hull) boldFont Font]
1680
1681    label $inner.lighting_l \
1682        -text "Lighting / Material Properties" \
1683        -font "Arial 9 bold"
1684
1685    checkbutton $inner.isosurface -text "Isosurface shading" -font $fg \
1686        -variable [itcl::scope _settings(-isosurfaceshading)] \
1687        -command [itcl::code $this AdjustSetting -isosurfaceshading]
1688
1689    checkbutton $inner.light2side -text "Two-sided lighting" -font $fg \
1690        -variable [itcl::scope _settings(-light2side)] \
1691        -command [itcl::code $this AdjustSetting -light2side]
1692
1693    checkbutton $inner.visibility -text "Visible" -font $fg \
1694        -variable [itcl::scope _settings(-volumevisible)] \
1695        -command [itcl::code $this AdjustSetting -volumevisible]
1696
1697    label $inner.ambient_l -text "Ambient" -font $fg
1698    ::scale $inner.ambient -from 0 -to 100 -orient horizontal \
1699        -variable [itcl::scope _settings(-ambient)] \
1700        -showvalue off -command [itcl::code $this AdjustSetting -ambient] \
1701        -troughcolor grey92
1702
1703    label $inner.diffuse_l -text "Diffuse" -font $fg
1704    ::scale $inner.diffuse -from 0 -to 100 -orient horizontal \
1705        -variable [itcl::scope _settings(-diffuse)] \
1706        -showvalue off -command [itcl::code $this AdjustSetting -diffuse] \
1707        -troughcolor grey92
1708
1709    label $inner.specularLevel_l -text "Specular" -font $fg
1710    ::scale $inner.specularLevel -from 0 -to 100 -orient horizontal \
1711        -variable [itcl::scope _settings(-specularlevel)] \
1712        -showvalue off \
1713        -command [itcl::code $this AdjustSetting -specularlevel] \
1714        -troughcolor grey92
1715
1716    label $inner.specularExponent_l -text "Shininess" -font $fg
1717    ::scale $inner.specularExponent -from 10 -to 128 -orient horizontal \
1718        -variable [itcl::scope _settings(-specularexponent)] \
1719        -showvalue off \
1720        -command [itcl::code $this AdjustSetting -specularexponent] \
1721        -troughcolor grey92
1722
1723    # Opacity
1724    label $inner.opacity_l -text "Opacity" -font $fg
1725    ::scale $inner.opacity -from 0 -to 100 -orient horizontal \
1726        -variable [itcl::scope _settings(-opacity)] \
1727        -showvalue off -command [itcl::code $this AdjustSetting -opacity] \
1728        -troughcolor grey92
1729
1730    label $inner.transferfunction_l \
1731        -text "Transfer Function" -font "Arial 9 bold"
1732
1733    # Tooth thickness
1734    label $inner.thin -text "Thin" -font $fg
1735    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
1736        -variable [itcl::scope _settings(-thickness)] \
1737        -showvalue off -command [itcl::code $this AdjustSetting -thickness] \
1738        -troughcolor grey92
1739    label $inner.thick -text "Thick" -font $fg
1740
1741    # Colormap
1742    label $inner.colormap_l -text "Colormap" -font $fg
1743    itk_component add colormap {
1744        Rappture::Combobox $inner.colormap -width 10 -editable no
1745    }
1746
1747    $inner.colormap choices insert end [GetColormapList -includeDefault -includeNone]
1748    bind $inner.colormap <<Value>> \
1749        [itcl::code $this AdjustSetting -colormap]
1750    $itk_component(colormap) value "default"
1751    set _settings(-colormap) "default"
1752
1753    # Component
1754    label $inner.volcomponents_l -text "Component" -font $fg
1755    itk_component add volcomponents {
1756        Rappture::Combobox $inner.volcomponents -editable no
1757    }
1758    bind $inner.volcomponents <<Value>> \
1759        [itcl::code $this AdjustSetting -current]
1760
1761    blt::table $inner \
1762        0,0 $inner.volcomponents_l -anchor e -cspan 2 \
1763        0,2 $inner.volcomponents -cspan 3 -fill x \
1764        1,1 $inner.lighting_l -anchor w -cspan 4 \
1765        2,1 $inner.ambient_l -anchor e \
1766        2,2 $inner.ambient -cspan 3 -fill x \
1767        3,1 $inner.diffuse_l -anchor e \
1768        3,2 $inner.diffuse -cspan 3 -fill x \
1769        4,1 $inner.specularLevel_l -anchor e \
1770        4,2 $inner.specularLevel -cspan 3 -fill x \
1771        5,1 $inner.specularExponent_l -anchor e \
1772        5,2 $inner.specularExponent -cspan 3 -fill x \
1773        6,1 $inner.light2side -cspan 3 -anchor w \
1774        7,1 $inner.visibility -cspan 3 -anchor w \
1775        8,1 $inner.transferfunction_l -anchor w -cspan 4 \
1776        9,1 $inner.opacity_l -anchor e \
1777        9,2 $inner.opacity -cspan 3 -fill x \
1778        10,1 $inner.colormap_l -anchor e \
1779        10,2 $inner.colormap -padx 2 -cspan 3 -fill x \
1780        11,1 $inner.thin -anchor e \
1781        11,2 $inner.thickness -cspan 2 -fill x \
1782        11,4 $inner.thick -anchor w
1783
1784    blt::table configure $inner c* r* -resize none
1785    blt::table configure $inner r* -pady { 2 0 }
1786    blt::table configure $inner c2 c3 r12 -resize expand
1787    blt::table configure $inner c0 -width .1i
1788}
1789
1790itcl::body Rappture::NanovisViewer::BuildCutplanesTab {} {
1791    set inner [$itk_component(main) insert end \
1792        -title "Cutplane Settings" \
1793        -icon [Rappture::icon cutbutton]]
1794    $inner configure -borderwidth 4
1795
1796    checkbutton $inner.visible \
1797        -text "Show Cutplanes" \
1798        -variable [itcl::scope _settings(-cutplanesvisible)] \
1799        -command [itcl::code $this AdjustSetting -cutplanesvisible] \
1800        -font "Arial 9"
1801
1802    # X-value slicer...
1803    itk_component add xCutButton {
1804        Rappture::PushButton $inner.xbutton \
1805            -onimage [Rappture::icon x-cutplane] \
1806            -offimage [Rappture::icon x-cutplane] \
1807            -command [itcl::code $this AdjustSetting -xcutplanevisible] \
1808            -variable [itcl::scope _settings(-xcutplanevisible)]
1809    }
1810    Rappture::Tooltip::for $itk_component(xCutButton) \
1811        "Toggle the X cut plane on/off"
1812    $itk_component(xCutButton) select
1813
1814    itk_component add xCutScale {
1815        ::scale $inner.xval -from 100 -to 0 \
1816            -width 10 -orient vertical -showvalue off \
1817            -borderwidth 1 -highlightthickness 0 \
1818            -command [itcl::code $this Slice move x] \
1819            -variable [itcl::scope _settings(-xcutplaneposition)]
1820    } {
1821        usual
1822        ignore -borderwidth -highlightthickness
1823    }
1824    # Set the default cutplane value before disabling the scale.
1825    $itk_component(xCutScale) set 50
1826    $itk_component(xCutScale) configure -state disabled
1827    Rappture::Tooltip::for $itk_component(xCutScale) \
1828        "@[itcl::code $this SlicerTip x]"
1829
1830    # Y-value slicer...
1831    itk_component add yCutButton {
1832        Rappture::PushButton $inner.ybutton \
1833            -onimage [Rappture::icon y-cutplane] \
1834            -offimage [Rappture::icon y-cutplane] \
1835            -command [itcl::code $this AdjustSetting -ycutplanevisible] \
1836            -variable [itcl::scope _settings(-ycutplanevisible)]
1837    }
1838    Rappture::Tooltip::for $itk_component(yCutButton) \
1839        "Toggle the Y cut plane on/off"
1840    $itk_component(yCutButton) select
1841
1842    itk_component add yCutScale {
1843        ::scale $inner.yval -from 100 -to 0 \
1844            -width 10 -orient vertical -showvalue off \
1845            -borderwidth 1 -highlightthickness 0 \
1846            -command [itcl::code $this Slice move y] \
1847            -variable [itcl::scope _settings(-ycutplaneposition)]
1848    } {
1849        usual
1850        ignore -borderwidth -highlightthickness
1851    }
1852    Rappture::Tooltip::for $itk_component(yCutScale) \
1853        "@[itcl::code $this SlicerTip y]"
1854    # Set the default cutplane value before disabling the scale.
1855    $itk_component(yCutScale) set 50
1856    $itk_component(yCutScale) configure -state disabled
1857
1858    # Z-value slicer...
1859    itk_component add zCutButton {
1860        Rappture::PushButton $inner.zbutton \
1861            -onimage [Rappture::icon z-cutplane] \
1862            -offimage [Rappture::icon z-cutplane] \
1863            -command [itcl::code $this AdjustSetting -zcutplanevisible] \
1864            -variable [itcl::scope _settings(-zcutplanevisible)]
1865    }
1866    Rappture::Tooltip::for $itk_component(zCutButton) \
1867        "Toggle the Z cut plane on/off"
1868    $itk_component(zCutButton) select
1869
1870    itk_component add zCutScale {
1871        ::scale $inner.zval -from 100 -to 0 \
1872            -width 10 -orient vertical -showvalue off \
1873            -borderwidth 1 -highlightthickness 0 \
1874            -command [itcl::code $this Slice move z] \
1875            -variable [itcl::scope _settings(-zcutplaneposition)]
1876    } {
1877        usual
1878        ignore -borderwidth -highlightthickness
1879    }
1880    $itk_component(zCutScale) set 50
1881    $itk_component(zCutScale) configure -state disabled
1882    Rappture::Tooltip::for $itk_component(zCutScale) \
1883        "@[itcl::code $this SlicerTip z]"
1884
1885    blt::table $inner \
1886        0,1 $inner.visible -anchor w -pady 2 -cspan 4 \
1887        1,1 $itk_component(xCutScale) \
1888        1,2 $itk_component(yCutScale) \
1889        1,3 $itk_component(zCutScale) \
1890        2,1 $itk_component(xCutButton) \
1891        2,2 $itk_component(yCutButton) \
1892        2,3 $itk_component(zCutButton)
1893
1894    blt::table configure $inner r0 r1 r2 c* -resize none
1895    blt::table configure $inner r3 c4 -resize expand
1896    blt::table configure $inner c0 -width 2
1897    blt::table configure $inner c1 c2 c3 -padx 2
1898}
1899
1900itcl::body Rappture::NanovisViewer::BuildCameraTab {} {
1901    set inner [$itk_component(main) insert end \
1902        -title "Camera Settings" \
1903        -icon [Rappture::icon camera]]
1904    $inner configure -borderwidth 4
1905
1906    label $inner.view_l -text "view" -font "Arial 9"
1907    set f [frame $inner.view]
1908    foreach side { front back left right top bottom } {
1909        button $f.$side  -image [Rappture::icon view$side] \
1910            -command [itcl::code $this SetOrientation $side]
1911        Rappture::Tooltip::for $f.$side "Change the view to $side"
1912        pack $f.$side -side left
1913    }
1914
1915    blt::table $inner \
1916        0,0 $inner.view_l -anchor e -pady 2 \
1917        0,1 $inner.view -anchor w -pady 2
1918    blt::table configure $inner r0 -resize none
1919
1920    set row 1
1921    set labels { qw qx qy qz xpan ypan zoom }
1922    foreach tag $labels {
1923        label $inner.${tag}label -text $tag -font "Arial 9"
1924        entry $inner.${tag} -font "Arial 9"  -bg white \
1925            -textvariable [itcl::scope _settings(-$tag)]
1926        bind $inner.${tag} <Return> \
1927            [itcl::code $this camera set -${tag}]
1928        bind $inner.${tag} <KP_Enter> \
1929            [itcl::code $this camera set -${tag}]
1930        blt::table $inner \
1931            $row,0 $inner.${tag}label -anchor e -pady 2 \
1932            $row,1 $inner.${tag} -anchor w -pady 2
1933        blt::table configure $inner r$row -resize none
1934        incr row
1935    }
1936
1937    blt::table configure $inner c* -resize none
1938    blt::table configure $inner c2 -resize expand
1939    blt::table configure $inner r$row -resize expand
1940}
1941
1942# ----------------------------------------------------------------------
1943# USAGE: Slice move x|y|z <newval>
1944#
1945# Called automatically when the user drags the slider to move the
1946# cut plane that slices 3D data.  Gets the current value from the
1947# slider and moves the cut plane to the appropriate point in the
1948# data set.
1949# ----------------------------------------------------------------------
1950itcl::body Rappture::NanovisViewer::Slice {option args} {
1951    switch -- $option {
1952        move {
1953            if {[llength $args] != 2} {
1954                error "wrong # args: should be \"Slice move x|y|z newval\""
1955            }
1956            set axis [lindex $args 0]
1957            set newval [lindex $args 1]
1958
1959            set newpos [expr {0.01*$newval}]
1960            set datasets [CurrentDatasets -cutplanes]
1961            set tag [lindex $datasets 0]
1962            SendCmd "cutplane position $newpos $axis $tag"
1963        }
1964        default {
1965            error "bad option \"$option\": should be axis, move, or volume"
1966        }
1967    }
1968}
1969
1970# ----------------------------------------------------------------------
1971# USAGE: SlicerTip <axis>
1972#
1973# Used internally to generate a tooltip for the x/y/z slicer controls.
1974# Returns a message that includes the current slicer value.
1975# ----------------------------------------------------------------------
1976itcl::body Rappture::NanovisViewer::SlicerTip {axis} {
1977    set val [$itk_component(${axis}CutScale) get]
1978    return "Move the [string toupper $axis] cut plane.\nCurrently:  $axis = $val%"
1979}
1980
1981itcl::body Rappture::NanovisViewer::DoResize {} {
1982    $_arcball resize $_width $_height
1983    SendCmd "screen size $_width $_height"
1984    set _resizePending 0
1985}
1986
1987itcl::body Rappture::NanovisViewer::EventuallyResize { w h } {
1988    set _width $w
1989    set _height $h
1990    $_arcball resize $w $h
1991    if { !$_resizePending } {
1992        $_dispatcher event -idle !resize
1993        set _resizePending 1
1994    }
1995}
1996
1997itcl::body Rappture::NanovisViewer::EventuallyRedrawLegend {} {
1998    if { !$_resizeLegendPending } {
1999        $_dispatcher event -idle !legend
2000        set _resizeLegendPending 1
2001    }
2002}
2003
2004#  camera --
2005#
2006itcl::body Rappture::NanovisViewer::camera {option args} {
2007    switch -- $option {
2008        "show" {
2009            puts [array get _view]
2010        }
2011        "set" {
2012            set what [lindex $args 0]
2013            set x $_settings($what)
2014            set code [catch { string is double $x } result]
2015            if { $code != 0 || !$result } {
2016                set _settings($what) $_view($what)
2017                return
2018            }
2019            switch -- $what {
2020                "-xpan" - "-ypan" {
2021                    set _view($what) $_settings($what)
2022                    PanCamera
2023                }
2024                "-qx" - "-qy" - "-qz" - "-qw" {
2025                    set _view($what) $_settings($what)
2026                    set q [ViewToQuaternion]
2027                    $_arcball quaternion $q
2028                    SendCmd "camera orient $q"
2029                }
2030                "-zoom" {
2031                    set _view($what) $_settings($what)
2032                    SendCmd "camera zoom $_view($what)"
2033                }
2034            }
2035        }
2036    }
2037}
2038
2039itcl::body Rappture::NanovisViewer::GetVtkData { args } {
2040    # FIXME: We can only put one component of one dataset in a single
2041    # VTK file.  To download all components/results, we would need
2042    # to put them in an archive (e.g. zip or tar file)
2043    if { $_first != "" && $_current != "" } {
2044        set bytes [$_first vtkdata $_current]
2045        return [list .vtk $bytes]
2046    }
2047    puts stderr "Failed to get vtkdata"
2048    return ""
2049}
2050
2051itcl::body Rappture::NanovisViewer::GetImage { args } {
2052    if { [image width $_image(download)] > 0 &&
2053         [image height $_image(download)] > 0 } {
2054        set bytes [$_image(download) data -format "jpeg -quality 100"]
2055        set bytes [Rappture::encoding::decode -as b64 $bytes]
2056        return [list .jpg $bytes]
2057    }
2058    return ""
2059}
2060
2061itcl::body Rappture::NanovisViewer::BuildDownloadPopup { popup command } {
2062    Rappture::Balloon $popup \
2063        -title "[Rappture::filexfer::label downloadWord] as..."
2064    set inner [$popup component inner]
2065    label $inner.summary -text "" -anchor w
2066
2067    radiobutton $inner.vtk_button -text "VTK data file" \
2068        -variable [itcl::scope _downloadPopup(format)] \
2069        -font "Arial 9" \
2070        -value vtk
2071    Rappture::Tooltip::for $inner.vtk_button "Save as VTK data file."
2072
2073    radiobutton $inner.image_button -text "Image File" \
2074        -variable [itcl::scope _downloadPopup(format)] \
2075        -font "Arial 9 " \
2076        -value image
2077    Rappture::Tooltip::for $inner.image_button \
2078        "Save as digital image."
2079
2080    button $inner.ok -text "Save" \
2081        -highlightthickness 0 -pady 2 -padx 3 \
2082        -command $command \
2083        -compound left \
2084        -image [Rappture::icon download]
2085
2086    button $inner.cancel -text "Cancel" \
2087        -highlightthickness 0 -pady 2 -padx 3 \
2088        -command [list $popup deactivate] \
2089        -compound left \
2090        -image [Rappture::icon cancel]
2091
2092    blt::table $inner \
2093        0,0 $inner.summary -cspan 2  \
2094        1,0 $inner.vtk_button -anchor w -cspan 2 -padx { 4 0 } \
2095        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
2096        4,1 $inner.cancel -width .9i -fill y \
2097        4,0 $inner.ok -padx 2 -width .9i -fill y
2098    blt::table configure $inner r3 -height 4
2099    blt::table configure $inner r4 -pady 4
2100    raise $inner.image_button
2101    $inner.vtk_button invoke
2102    return $inner
2103}
2104
2105itcl::body Rappture::NanovisViewer::SetOrientation { side } {
2106    array set positions {
2107        front "1 0 0 0"
2108        back  "0 0 1 0"
2109        left  "0.707107 0 -0.707107 0"
2110        right "0.707107 0 0.707107 0"
2111        top   "0.707107 -0.707107 0 0"
2112        bottom "0.707107 0.707107 0 0"
2113    }
2114    foreach name { -qw -qx -qy -qz } value $positions($side) {
2115        set _view($name) $value
2116    }
2117    set q [ViewToQuaternion]
2118    $_arcball quaternion $q
2119    SendCmd "camera orient $q"
2120    SendCmd "camera reset"
2121    set _view(-xpan) 0
2122    set _view(-ypan) 0
2123    set _view(-zoom) 1.0
2124    set _settings(-xpan) $_view(-xpan)
2125    set _settings(-ypan) $_view(-ypan)
2126    set _settings(-zoom) $_view(-zoom)
2127}
2128
2129#
2130# InitComponentSettings --
2131#
2132#    Initializes the volume settings for a specific component. This should
2133#    match what's used as global settings above. This is called the first
2134#    time we try to switch to a given component in SwitchComponent below.
2135#
2136itcl::body Rappture::NanovisViewer::InitComponentSettings { cname } {
2137    foreach {key value} {
2138        -ambient           60
2139        -colormap          "default"
2140        -diffuse           40
2141        -light2side        1
2142        -opacity           50
2143        -specularexponent  90
2144        -specularlevel     30
2145        -thickness         350
2146        -volumevisible     1
2147    } {
2148        set _settings($cname${key}) $value
2149    }
2150}
2151
2152#
2153# SwitchComponent --
2154#
2155#    This is called when the current component is changed by the dropdown
2156#    menu in the volume tab.  It synchronizes the global volume settings
2157#    with the settings of the new current component.
2158#
2159itcl::body Rappture::NanovisViewer::SwitchComponent { cname } {
2160    if { ![info exists _settings($cname-ambient)] } {
2161        InitComponentSettings $cname
2162    }
2163    # _settings variables change widgets, except for colormap
2164    set _settings(-ambient)          $_settings($cname-ambient)
2165    set _settings(-colormap)         $_settings($cname-colormap)
2166    set _settings(-diffuse)          $_settings($cname-diffuse)
2167    set _settings(-light2side)       $_settings($cname-light2side)
2168    set _settings(-opacity)          $_settings($cname-opacity)
2169    set _settings(-specularexponent) $_settings($cname-specularexponent)
2170    set _settings(-specularlevel)    $_settings($cname-specularlevel)
2171    set _settings(-thickness)        $_settings($cname-thickness)
2172    set _settings(-volumevisible)    $_settings($cname-volumevisible)
2173    $itk_component(colormap) value   $_settings($cname-colormap)
2174    set _current $cname;                # Reset the current component
2175}
2176
2177#
2178# BuildVolumeComponents --
2179#
2180#    This is called from the "scale" method which is called when a new
2181#    dataset is added or deleted.  It repopulates the dropdown menu of
2182#    volume component names.  It sets the current component to the first
2183#    component in the list (of components found).  Finally, if there is
2184#    only one component, don't display the label or the combobox in the
2185#    volume settings tab.
2186#
2187itcl::body Rappture::NanovisViewer::BuildVolumeComponents {} {
2188    $itk_component(volcomponents) choices delete 0 end
2189    foreach name $_componentsList {
2190        $itk_component(volcomponents) choices insert end $name $name
2191    }
2192    set _current [lindex $_componentsList 0]
2193    $itk_component(volcomponents) value $_current
2194    set parent [winfo parent $itk_component(volcomponents)]
2195    if { [llength $_componentsList] <= 1 } {
2196        # Unpack the components label and dropdown if there's only one
2197        # component.
2198        blt::table forget $parent.volcomponents_l $parent.volcomponents
2199    } else {
2200        # Pack the components label and dropdown into the table there's
2201        # more than one component to select.
2202        blt::table $parent \
2203            0,0 $parent.volcomponents_l -anchor e -cspan 2 \
2204            0,2 $parent.volcomponents -cspan 3 -fill x
2205    }
2206}
2207
2208#
2209# GetDatasetsWithComponents --
2210#
2211#    Returns a list of all the datasets (known by the combination of their
2212#    data object and component name) that match the given component name.
2213#    For example, this is used where we want to change the settings of
2214#    volumes that have the current component.
2215#
2216itcl::body Rappture::NanovisViewer::GetDatasetsWithComponent { cname } {
2217    if { ![info exists _volcomponents($cname)] } {
2218        return ""
2219    }
2220    set list ""
2221    foreach tag $_volcomponents($cname) {
2222        if { ![info exists _serverDatasets($tag)] } {
2223            continue
2224        }
2225        lappend list $tag
2226    }
2227    return $list
2228}
2229
2230#
2231# HideAllMarkers --
2232#
2233#    Hide all the markers in all the transfer functions.  Can't simply
2234#    delete and recreate markers from the <style> since the user may have
2235#    created, deleted, or moved markers.
2236#
2237itcl::body Rappture::NanovisViewer::HideAllMarkers {} {
2238    foreach cname [array names _transferFunctionEditors] {
2239        $_transferFunctionEditors($cname) hideMarkers
2240    }
2241}
2242
2243itcl::body Rappture::NanovisViewer::GetColormap { cname color } {
2244    if { $color == "default" } {
2245        return $_cname2defaultcolormap($cname)
2246    }
2247    return [ColorsToColormap $color]
2248}
2249
2250itcl::body Rappture::NanovisViewer::ResetColormap { cname color } {
2251    # Get the current transfer function
2252    if { ![info exists _cname2transferFunction($cname)] } {
2253        return
2254    }
2255    foreach { cmap amap } $_cname2transferFunction($cname) break
2256    set cmap [GetColormap $cname $color]
2257    set _cname2transferFunction($cname) [list $cmap $amap]
2258    SendCmd [list transfunc define $cname $cmap $amap]
2259    EventuallyRedrawLegend
2260}
2261
2262itcl::body Rappture::NanovisViewer::ComputeAlphamap { cname } {
2263    if { ![info exists _transferFunctionEditors($cname)] } {
2264        return [list 0.0 0.0 1.0 1.0]
2265    }
2266    if { ![info exists _settings($cname-ambient)] } {
2267        InitComponentSettings $cname
2268    }
2269
2270    set isovalues [$_transferFunctionEditors($cname) values]
2271
2272    # Transfer function should be normalized with [0,1] range
2273    # The volume shading opacity setting is used to scale opacity
2274    # in the volume shader.
2275    set max 1.0
2276
2277    # Use the component-wise thickness setting from the slider
2278    # settings widget
2279    # Scale values between 0.00001 and 0.01000
2280    set delta [expr {double($_settings($cname-thickness)) * 0.0001}]
2281
2282    set first [lindex $isovalues 0]
2283    set last [lindex $isovalues end]
2284    set amap ""
2285    if { $first == "" || $first != 0.0 } {
2286        lappend amap 0.0 0.0
2287    }
2288    foreach x $isovalues {
2289        set x1 [expr {$x-$delta-0.00001}]
2290        set x2 [expr {$x-$delta}]
2291        set x3 [expr {$x+$delta}]
2292        set x4 [expr {$x+$delta+0.00001}]
2293        if { $x1 < 0.0 } {
2294            set x1 0.0
2295        } elseif { $x1 > 1.0 } {
2296            set x1 1.0
2297        }
2298        if { $x2 < 0.0 } {
2299            set x2 0.0
2300        } elseif { $x2 > 1.0 } {
2301            set x2 1.0
2302        }
2303        if { $x3 < 0.0 } {
2304            set x3 0.0
2305        } elseif { $x3 > 1.0 } {
2306            set x3 1.0
2307        }
2308        if { $x4 < 0.0 } {
2309            set x4 0.0
2310        } elseif { $x4 > 1.0 } {
2311            set x4 1.0
2312        }
2313        # add spikes in the middle
2314        lappend amap $x1 0.0
2315        lappend amap $x2 $max
2316        lappend amap $x3 $max
2317        lappend amap $x4 0.0
2318    }
2319    if { $last == "" || $last != 1.0 } {
2320        lappend amap 1.0 0.0
2321    }
2322    return $amap
2323}
2324
2325itcl::body Rappture::NanovisViewer::SetObjectStyle { dataobj cname } {
2326    array set style {
2327        -opacity 0.5
2328    }
2329    array set style [lindex [$dataobj components -style $cname] 0]
2330    # Some tools erroneously set -opacity to 1 in style, so
2331    # override the requested opacity for now
2332    set style(-opacity) 0.5
2333    set _settings($cname-opacity) [expr $style(-opacity) * 100.0]
2334    set tag $dataobj-$cname
2335    SendCmd "volume shading opacity $style(-opacity) $tag"
2336    NameTransferFunction $dataobj $cname
2337}
Note: See TracBrowser for help on using the repository browser.