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

Last change on this file since 5298 was 5298, checked in by ldelgass, 4 years ago

fix for SetObjectStyle?

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