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

Last change on this file since 5261 was 5261, checked in by ldelgass, 5 years ago

whitespace

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