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

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

whitespace

File size: 81.1 KB
RevLine 
[5098]1# -*- mode: tcl; indent-tabs-mode: nil -*-
[1254]2# ----------------------------------------------------------------------
[436]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
[3177]10#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
[436]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
[3485]18
[1549]19#
20# FIXME:
[5098]21#       Need to Add DX readers this client to examine the data before
[2744]22#       it's sent to the server.  This will eliminate 90% of the insanity in
[5098]23#       computing the limits of all the volumes.  I can rip out all the
[2744]24#       "receive data" "send transfer function" event crap.
[1549]25#
26#       This means we can compute the transfer function (relative values) and
[2744]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
[5098]31#       the volume.
[1549]32#
[436]33option add *NanovisViewer.width 4i widgetDefault
[878]34option add *NanovisViewer*cursor crosshair widgetDefault
[436]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 \
[676]43    -*-helvetica-medium-r-normal-*-12-* widgetDefault
[436]44
[907]45# must use this name -- plugs into Rappture::resources::load
46proc NanovisViewer_init_resources {} {
47    Rappture::resources::register \
[1694]48        nanovis_server Rappture::NanovisViewer::SetServerList
[907]49}
50
[436]51itcl::class Rappture::NanovisViewer {
[907]52    inherit Rappture::VisViewer
[1254]53
[436]54    itk_option define -plotforeground plotForeground Foreground ""
55    itk_option define -plotbackground plotBackground Background ""
56    itk_option define -plotoutline plotOutline PlotOutline ""
[1254]57
58    constructor { hostlist args } {
[1694]59        Rappture::VisViewer::constructor $hostlist
[1254]60    } {
[1694]61        # defined below
[907]62    }
[1254]63    destructor {
[1694]64        # defined below
[907]65    }
[935]66    public proc SetServerList { namelist } {
[1694]67        Rappture::VisViewer::SetServerList "nanovis" $namelist
[935]68    }
[436]69    public method add {dataobj {settings ""}}
[1435]70    public method camera {option args}
[436]71    public method delete {args}
[1514]72    public method disconnect {}
[464]73    public method download {option args}
[1435]74    public method get {args}
75    public method isconnected {}
[5098]76    public method parameters {title args} {
77        # do nothing
[907]78    }
[1435]79    public method scale {args}
[3930]80    public method updateTransferFunctions {}
[436]81
[4509]82    # The following methods are only used by this class.
[5098]83    private method AddNewMarker { x y }
[3940]84    private method AdjustSetting {what {value ""}}
[1435]85    private method BuildCameraTab {}
[1376]86    private method BuildCutplanesTab {}
87    private method BuildViewTab {}
[3940]88    private method BuildVolumeComponents {}
[1376]89    private method BuildVolumeTab {}
[5098]90    private method ComputeAlphamap { cname }
[3940]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 }
[5098]97    private method EventuallyRedrawLegend { }
98    private method EventuallyResize { w h }
[3940]99    private method FixLegend {}
[5098]100    private method GetColormap { cname color }
101    private method GetDatasetsWithComponent { cname }
102    private method HideAllMarkers {}
103    private method InitComponentSettings { cname }
[3940]104    private method InitSettings { args }
105    private method NameTransferFunction { dataobj comp }
106    private method Pan {option x y}
[1376]107    private method PanCamera {}
[3940]108    private method ParseLevelsOption { cname levels }
109    private method ParseMarkersOption { cname markers }
[5098]110    private method QuaternionToView { q } {
[4765]111        foreach { _view(-qw) _view(-qx) _view(-qy) _view(-qz) } $q break
112    }
[3940]113    private method Rebuild {}
114    private method ReceiveData { args }
115    private method ReceiveImage { args }
116    private method ReceiveLegend { tf vmin vmax size }
[4509]117    private method RemoveMarker { x y }
[3940]118    private method ResetColormap { cname color }
119    private method Rotate {option x y}
120    private method SendTransferFunctions {}
[5290]121    private method SetObjectStyle { dataobj cname }
[3517]122    private method SetOrientation { side }
[3940]123    private method Slice {option args}
124    private method SlicerTip {axis}
[5098]125    private method SwitchComponent { cname }
126    private method ViewToQuaternion {} {
[4192]127        return [list $_view(-qw) $_view(-qx) $_view(-qy) $_view(-qz)]
128    }
[4765]129    private method Zoom {option}
[1376]130
[3362]131    private variable _arcball ""
[5098]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
[5261]136    private variable _recvdDatasets    ;# list of data objs to send to server
[447]137
[5261]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
[3940]142    private variable _parsedFunction
[4509]143    private variable _transferFunctionEditors
[4559]144    private variable _settings
[5261]145    private variable _first ""         ;# This is the topmost volume.
146    private variable _current ""       ;# Currently selected component
[5292]147    private variable _volcomponents    ;# Maps component name to list of
148                                       ;# dataobj-component tags
149    private variable _componentsList   ;# List of components found
[3940]150    private variable _cname2transferFunction
[3925]151    private variable _cname2defaultcolormap
[1435]152    private variable _width 0
153    private variable _height 0
[1448]154    private variable _resizePending 0
155    private variable _resizeLegendPending 0
[5292]156
157    common _downloadPopup              ;# download options from popup
158    private common _hardcopy
[436]159}
160
161itk::usual NanovisViewer {
162    keep -background -foreground -cursor -font
163    keep -plotbackground -plotforeground
164}
165
166# ----------------------------------------------------------------------
167# CONSTRUCTOR
168# ----------------------------------------------------------------------
[462]169itcl::body Rappture::NanovisViewer::constructor {hostlist args} {
[2671]170    set _serverType "nanovis"
[907]171
172    # Draw legend event
[456]173    $_dispatcher register !legend
[1376]174    $_dispatcher dispatch $this !legend "[itcl::code $this FixLegend]; list"
[1435]175
[907]176    # Send transfer functions event
[972]177    $_dispatcher register !send_transfunc
178    $_dispatcher dispatch $this !send_transfunc \
[3940]179        "[itcl::code $this SendTransferFunctions]; list"
[1435]180
[907]181    # Rebuild event
182    $_dispatcher register !rebuild
[1376]183    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
[456]184
[1435]185    # Resize event
[1377]186    $_dispatcher register !resize
187    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
188
[436]189    #
[907]190    # Populate parser with commands handle incoming requests
[436]191    #
[1376]192    $_parser alias image [itcl::code $this ReceiveImage]
193    $_parser alias legend [itcl::code $this ReceiveLegend]
194    $_parser alias data [itcl::code $this ReceiveData]
[436]195
[919]196    # Initialize the view to some default parameters.
[1435]197    array set _view {
[4188]198        -qw      0.853553
199        -qx      -0.353553
200        -qy      0.353553
201        -qz      0.146447
202        -xpan    0
203        -ypan    0
[4509]204        -zoom    1.0
[919]205    }
[3362]206    set _arcball [blt::arcball create 100 100]
[4188]207    $_arcball quaternion [ViewToQuaternion]
[3362]208
[3930]209    set _limits(v) [list 0.0 1.0]
[3330]210    set _reset 1
[436]211
[4188]212    array set _settings {
213        -ambient                60
[4195]214        -axesvisible            1
[4559]215        -background             black
[4709]216        -colormap               "default"
[4468]217        -cutplanesvisible       0
[4188]218        -diffuse                40
[4195]219        -gridvisible            0
220        -isosurfaceshading      0
221        -legendvisible          1
[4188]222        -light2side             1
[4191]223        -opacity                50
224        -outlinevisible         0
[4188]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
[4509]234        -xcutplaneposition      50
[4188]235        -xcutplanevisible       1
236        -xpan                   0
[4509]237        -ycutplaneposition      50
[4188]238        -ycutplanevisible       1
239        -ypan                   0
[4509]240        -zcutplaneposition      50
[4188]241        -zcutplanevisible       1
242        -zoom                   1.0
243    }
[436]244
[1545]245    itk_component add 3dview {
[2584]246        label $itk_component(plotarea).view -image $_image(plot) \
[1694]247            -highlightthickness 0 -borderwidth 0
[1545]248    } {
[1694]249        usual
250        ignore -highlightthickness -borderwidth  -background
[1545]251    }
[3330]252    bind $itk_component(3dview) <Control-F1> [itcl::code $this ToggleConsole]
[1545]253
[1376]254    set f [$itk_component(main) component controls]
[436]255    itk_component add reset {
[1376]256        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
[1391]257            -highlightthickness 0 \
[1376]258            -image [Rappture::icon reset-view] \
259            -command [itcl::code $this Zoom reset]
[1391]260    } {
261        usual
262        ignore -highlightthickness
[436]263    }
[1376]264    pack $itk_component(reset) -side top -padx 2 -pady 2
[4188]265    Rappture::Tooltip::for $itk_component(reset) \
266        "Reset the view to the default zoom level"
[436]267
268    itk_component add zoomin {
[1376]269        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
[1391]270            -highlightthickness 0 \
[1376]271            -image [Rappture::icon zoom-in] \
272            -command [itcl::code $this Zoom in]
[1391]273    } {
274        usual
275        ignore -highlightthickness
[436]276    }
[1376]277    pack $itk_component(zoomin) -side top -padx 2 -pady 2
[436]278    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
279
280    itk_component add zoomout {
[1376]281        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
[1391]282            -highlightthickness 0 \
[1376]283            -image [Rappture::icon zoom-out] \
284            -command [itcl::code $this Zoom out]
[1391]285    } {
286        usual
287        ignore -highlightthickness
[436]288    }
[1376]289    pack $itk_component(zoomout) -side top -padx 2 -pady 2
[436]290    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
291
[447]292    itk_component add volume {
[1435]293        Rappture::PushButton $f.volume \
[1694]294            -onimage [Rappture::icon volume-on] \
295            -offimage [Rappture::icon volume-off] \
[4188]296            -command [itcl::code $this AdjustSetting -volume] \
297            -variable [itcl::scope _settings(-volume)]
[447]298    }
[1435]299    $itk_component(volume) select
[447]300    Rappture::Tooltip::for $itk_component(volume) \
[1376]301        "Toggle the volume cloud on/off"
[1435]302    pack $itk_component(volume) -padx 2 -pady 2
[447]303
[3899]304    itk_component add cutplane {
305        Rappture::PushButton $f.cutplane \
306            -onimage [Rappture::icon cutbutton] \
307            -offimage [Rappture::icon cutbutton] \
[4188]308            -variable [itcl::scope _settings(-cutplanesvisible)] \
[5098]309            -command [itcl::code $this AdjustSetting -cutplanesvisible]
[3899]310    }
311    Rappture::Tooltip::for $itk_component(cutplane) \
312        "Show/Hide cutplanes"
313    pack $itk_component(cutplane) -padx 2 -pady 2
314
[3492]315    if { [catch {
316        BuildViewTab
317        BuildVolumeTab
318        BuildCutplanesTab
319        BuildCameraTab
320    } errs] != 0 } {
[5098]321        global errorInfo
[3492]322        puts stderr "errs=$errs errorInfo=$errorInfo"
323    }
[447]324
[907]325    # Legend
[456]326    set _image(legend) [image create photo]
327    itk_component add legend {
[1694]328        canvas $itk_component(plotarea).legend -height 50 -highlightthickness 0
[456]329    } {
[1694]330        usual
331        ignore -highlightthickness
332        rename -background -plotbackground plotBackground Background
[456]333    }
[1377]334    bind $itk_component(legend) <Configure> \
[3925]335        [itcl::code $this EventuallyRedrawLegend]
[4088]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)]
[1377]340
[5098]341    # Hack around the Tk panewindow.  The problem is that the requested
[1376]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.
[1435]344    set w 10000
[1376]345    pack forget $itk_component(3dview)
346    blt::table $itk_component(plotarea) \
[1694]347        0,0 $itk_component(3dview) -fill both -reqwidth $w \
[5098]348        1,0 $itk_component(legend) -fill x
[1376]349    blt::table configure $itk_component(plotarea) r1 -resize none
[456]350
[1228]351    # Bindings for rotation via mouse
[1215]352    bind $itk_component(3dview) <ButtonPress-1> \
[1694]353        [itcl::code $this Rotate click %x %y]
[459]354    bind $itk_component(3dview) <B1-Motion> \
[1694]355        [itcl::code $this Rotate drag %x %y]
[1215]356    bind $itk_component(3dview) <ButtonRelease-1> \
[1694]357        [itcl::code $this Rotate release %x %y]
[459]358    bind $itk_component(3dview) <Configure> \
[1694]359        [itcl::code $this EventuallyResize %w %h]
[436]360
[1228]361    # Bindings for panning via mouse
[1215]362    bind $itk_component(3dview) <ButtonPress-2> \
[1694]363        [itcl::code $this Pan click %x %y]
[1215]364    bind $itk_component(3dview) <B2-Motion> \
[1694]365        [itcl::code $this Pan drag %x %y]
[1215]366    bind $itk_component(3dview) <ButtonRelease-2> \
[1694]367        [itcl::code $this Pan release %x %y]
[1215]368
[1228]369    # Bindings for panning via keyboard
370    bind $itk_component(3dview) <KeyPress-Left> \
[1694]371        [itcl::code $this Pan set -10 0]
[1228]372    bind $itk_component(3dview) <KeyPress-Right> \
[1694]373        [itcl::code $this Pan set 10 0]
[1228]374    bind $itk_component(3dview) <KeyPress-Up> \
[1694]375        [itcl::code $this Pan set 0 -10]
[1228]376    bind $itk_component(3dview) <KeyPress-Down> \
[1694]377        [itcl::code $this Pan set 0 10]
[1228]378    bind $itk_component(3dview) <Shift-KeyPress-Left> \
[1694]379        [itcl::code $this Pan set -2 0]
[1228]380    bind $itk_component(3dview) <Shift-KeyPress-Right> \
[1694]381        [itcl::code $this Pan set 2 0]
[1228]382    bind $itk_component(3dview) <Shift-KeyPress-Up> \
[1694]383        [itcl::code $this Pan set 0 -2]
[1228]384    bind $itk_component(3dview) <Shift-KeyPress-Down> \
[1694]385        [itcl::code $this Pan set 0 2]
[1228]386
387    # Bindings for zoom via keyboard
388    bind $itk_component(3dview) <KeyPress-Prior> \
[1694]389        [itcl::code $this Zoom out]
[1228]390    bind $itk_component(3dview) <KeyPress-Next> \
[1694]391        [itcl::code $this Zoom in]
[1228]392
393    bind $itk_component(3dview) <Enter> "focus $itk_component(3dview)"
394
[1215]395    if {[string equal "x11" [tk windowingsystem]]} {
[1694]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]
[1215]399    }
400
[459]401    set _image(download) [image create photo]
402
[436]403    eval itk_initialize $args
404
[5094]405    EnableWaitDialog 900
[907]406    Connect
[436]407}
408
409# ----------------------------------------------------------------------
410# DESTRUCTOR
411# ----------------------------------------------------------------------
412itcl::body Rappture::NanovisViewer::destructor {} {
[907]413    $_dispatcher cancel !rebuild
[972]414    $_dispatcher cancel !send_transfunc
[1448]415    $_dispatcher cancel !resize
[456]416    image delete $_image(plot)
417    image delete $_image(legend)
[459]418    image delete $_image(download)
[5140]419    foreach cname [array names _transferFunctionEditors] {
[3940]420        itcl::delete object $_transferFunctionEditors($cname)
421    }
[3362]422    catch { blt::arcball destroy $_arcball }
[5098]423    array unset _settings
[436]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 ""}} {
[3571]434    if { ![$dataobj isvalid] } {
435        return;                         # Object doesn't contain valid data.
436    }
[436]437    array set params {
[1694]438        -color auto
439        -width 1
440        -linestyle solid
441        -brightness 0
442        -raise 0
443        -description ""
444        -param ""
[436]445    }
[3799]446    array set params $settings
447
[436]448    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
[1694]449        # can't handle -autocolors yet
450        set params(-color) black
[436]451    }
[3813]452    set pos [lsearch -exact $_dlist $dataobj]
[436]453    if {$pos < 0} {
[1694]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
[436]459    }
460}
461
462# ----------------------------------------------------------------------
[572]463# USAGE: get ?-objects?
464# USAGE: get ?-image 3dview|legend?
[436]465#
466# Clients use this to query the list of objects being plotted, in
[572]467# order from bottom to top of this result.  The optional "-image"
468# flag can also request the internal images being shown.
[436]469# ----------------------------------------------------------------------
[572]470itcl::body Rappture::NanovisViewer::get {args} {
471    if {[llength $args] == 0} {
[1694]472        set args "-objects"
[572]473    }
474
475    set op [lindex $args 0]
476    switch -- $op {
[5210]477        -objects {
478            # put the dataobj list in order according to -raise options
479            set dlist $_dlist
[5292]480            foreach dataobj $dlist {
481                if {[info exists _obj2ovride($dataobj-raise)] &&
482                    $_obj2ovride($dataobj-raise)} {
483                    set i [lsearch -exact $dlist $dataobj]
[5210]484                    if {$i >= 0} {
485                        set dlist [lreplace $dlist $i $i]
[5292]486                        lappend dlist $dataobj
[5210]487                    }
[1694]488                }
489            }
[5210]490            return $dlist
[1694]491        }
[5210]492        -image {
493            if {[llength $args] != 2} {
494                error "wrong # args: should be \"get -image 3dview|legend\""
[1694]495            }
[5210]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                }
[1694]506            }
507        }
[5210]508        default {
509            error "bad option \"$op\": should be -objects or -image"
510        }
[436]511    }
512}
513
514# ----------------------------------------------------------------------
515# USAGE: delete ?<dataobj1> <dataobj2> ...?
516#
[1254]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.
[1141]520#
[436]521# ----------------------------------------------------------------------
522itcl::body Rappture::NanovisViewer::delete {args} {
523    if {[llength $args] == 0} {
[1694]524        set args $_dlist
[436]525    }
[1141]526    # Delete all specified dataobjs
[436]527    set changed 0
528    foreach dataobj $args {
[1694]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        }
[436]535    }
[1141]536    # If anything changed, then rebuild the plot
[436]537    if {$changed} {
[1694]538        $_dispatcher event -idle !rebuild
[436]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} {
[5188]552    array set style {
[4709]553        -color    BCGYR
554        -levels   6
555        -markers  ""
[436]556    }
[5098]557    array unset _limits
558    array unset _volcomponents
[3571]559    foreach dataobj $args {
560        if { ![$dataobj isvalid] } {
561            continue;                     # Object doesn't contain valid data.
562        }
[3917]563        foreach cname [$dataobj components] {
[3925]564            if { ![info exists _volcomponents($cname)] } {
565                lappend _componentsList $cname
[5188]566                array set style [lindex [$dataobj components -style $cname] 0]
567                set cmap [ColorsToColormap $style(-color)]
[3925]568                set _cname2defaultcolormap($cname) $cmap
[5188]569                set _settings($cname-colormap) $style(-color)
[3925]570            }
[3917]571            lappend _volcomponents($cname) $dataobj-$cname
[3930]572            array unset limits
573            array set limits [$dataobj valueLimits $cname]
574            set _limits($cname) $limits(v)
[3917]575        }
[1694]576        foreach axis {x y z v} {
[3571]577            foreach { min max } [$dataobj limits $axis] break
[1694]578            if {"" != $min && "" != $max} {
[3930]579                if { ![info exists _limits($axis)] } {
580                    set _limits($axis) [list $min $max]
[4776]581                    continue
[1694]582                }
[4776]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]
[1694]591            }
592        }
[436]593    }
[3917]594    BuildVolumeComponents
[436]595}
596
597# ----------------------------------------------------------------------
598# USAGE: download coming
[464]599# USAGE: download controls <downloadCommand>
[436]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# ----------------------------------------------------------------------
[464]607itcl::body Rappture::NanovisViewer::download {option args} {
[436]608    switch $option {
[1694]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.
[5098]626            if { [image width $_image(plot)] > 0 &&
[1694]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        }
[436]637    }
638}
639
640# ----------------------------------------------------------------------
[907]641# USAGE: Connect ?<host:port>,<host:port>...?
[436]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# ----------------------------------------------------------------------
[907]647itcl::body Rappture::NanovisViewer::Connect {} {
648    set _hosts [GetServerList "nanovis"]
649    if { "" == $_hosts } {
[1694]650        return 0
[436]651    }
[5098]652    set _reset 1
[907]653    set result [VisViewer::Connect $_hosts]
[1141]654    if { $result } {
[3592]655        if { $_reportClientInfo }  {
656            # Tell the server the viewer, hub, user and session.
[4075]657            # Do this immediately on connect before buffering any commands
[3592]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            }
[4669]669            lappend info "version" "$Rappture::version"
670            lappend info "build" "$Rappture::build"
671            lappend info "svnurl" "$Rappture::svnurl"
672            lappend info "installdir" "$Rappture::installdir"
[3592]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
[1694]680        set w [winfo width $itk_component(3dview)]
681        set h [winfo height $itk_component(3dview)]
682        EventuallyResize $w $h
[1141]683    }
[907]684    return $result
685}
[436]686
[907]687#
[919]688# isconnected --
689#
[921]690#       Indicates if we are currently connected to the visualization server.
[919]691#
[907]692itcl::body Rappture::NanovisViewer::isconnected {} {
693    return [VisViewer::IsConnected]
[436]694}
695
696#
[1514]697# disconnect --
698#
699itcl::body Rappture::NanovisViewer::disconnect {} {
700    Disconnect
701}
702
703#
[919]704# Disconnect --
705#
[921]706#       Clients use this method to disconnect from the current rendering
707#       server.
[919]708#
[907]709itcl::body Rappture::NanovisViewer::Disconnect {} {
710    VisViewer::Disconnect
[447]711
[459]712    # disconnected -- no more data sitting on server
[3339]713    array unset _serverDatasets
[436]714}
715
[878]716# ----------------------------------------------------------------------
[3940]717# USAGE: SendTransferFunctions
[878]718# ----------------------------------------------------------------------
[3940]719itcl::body Rappture::NanovisViewer::SendTransferFunctions {} {
720    foreach cname [array names _volcomponents] {
721        ComputeTransferFunction $cname
722    }
[1435]723    FixLegend
[447]724}
725
726# ----------------------------------------------------------------------
[1376]727# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
[436]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# ----------------------------------------------------------------------
[1376]733itcl::body Rappture::NanovisViewer::ReceiveImage { args } {
[1349]734    array set info {
[1694]735        -token "???"
736        -bytes 0
737        -type image
[1349]738    }
739    array set info $args
740    set bytes [ReceiveBytes $info(-bytes)]
[1435]741    ReceiveEcho <<line "<read $info(-bytes) bytes"
[1349]742    if { $info(-type) == "image" } {
[5098]743        ReceiveEcho "for [image width $_image(plot)]x[image height $_image(plot)] image>"
[1694]744        $_image(plot) configure -data $bytes
[3632]745    } elseif { $info(-type) == "print" } {
[1694]746        set tag $this-print-$info(-token)
747        set _hardcopy($tag) $bytes
[436]748    }
749}
750
[456]751#
[3930]752# DrawLegend --
[1013]753#
[3930]754itcl::body Rappture::NanovisViewer::DrawLegend { cname } {
[1013]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}]
[4088]760    if {"" == [$c find withtag colorbar]} {
[1694]761        $c create image 10 10 -anchor nw \
[4088]762            -image $_image(legend) -tags colorbar
[1694]763        $c create text $lx $ly -anchor sw \
[3930]764            -fill $itk_option(-plotforeground) -tags "limits text vmin"
[1694]765        $c create text [expr {$w-$lx}] $ly -anchor se \
[3930]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"
[4088]769        $c lower colorbar
770        $c bind colorbar <ButtonRelease-1> [itcl::code $this AddNewMarker %x %y]
[1013]771    }
[1247]772
[3940]773    # Display the markers used by the current transfer function.
774    HideAllMarkers
775    $_transferFunctionEditors($cname) showMarkers $_limits($cname)
776
[3930]777    foreach {min max} $_limits($cname) break
[4402]778    $c itemconfigure vmin -text [format %g $min]
[1013]779    $c coords vmin $lx $ly
[1254]780
[4402]781    $c itemconfigure vmax -text [format %g $max]
[1013]782    $c coords vmax [expr {$w-$lx}] $ly
[456]783
[3930]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
[3925]791
[2733]792    # The colormap may have changed. Resync the slicers with the colormap.
[3339]793    set datasets [CurrentDatasets -cutplanes]
[2733]794
795    # Adjust the cutplane for only the first component in the topmost volume
796    # (i.e. the first volume designated in the field).
[3339]797    set tag [lindex $datasets 0]
[2733]798    foreach axis {x y z} {
[2744]799        # Turn off cutplanes for all volumes
800        SendCmd "cutplane state 0 $axis"
[4188]801        if { $_settings(-${axis}cutplanevisible) } {
[2744]802            # Turn on cutplane for this particular volume and set the position
[3339]803            SendCmd "cutplane state 1 $axis $tag"
[4188]804            set pos [expr {0.01*$_settings(-${axis}cutplaneposition)}]
[3339]805            SendCmd "cutplane position $pos $axis $tag"
[2744]806        }
[2733]807    }
[1013]808}
[459]809
[1013]810#
[3930]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
[5098]815#       the slave interpreter.  The purpose is to collect data of the image
[3940]816#       representing the legend in the canvas.  In addition, the
817#       active transfer function is displayed.
[3930]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#
[1376]831# ReceiveData --
[1013]832#
[1254]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
[3339]839#       the data we've sent (tracked by _recvdDatasets) we can then determine
[1254]840#       what the transfer functions are for these volumes.
[1013]841#
842#
[1254]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.
[1013]850#
[1376]851itcl::body Rappture::NanovisViewer::ReceiveData { args } {
[1013]852    if { ![isconnected] } {
[1694]853        return
[1013]854    }
[3362]855
[1013]856    # Arguments from server are name value pairs. Stuff them in an array.
857    array set info $args
[968]858
[1435]859    set tag $info(tag)
860    set parts [split $tag -]
[1013]861
[1435]862    #
863    # Volumes don't exist until we're told about them.
864    #
865    set dataobj [lindex $parts 0]
[3339]866    set _serverDatasets($tag) 1
[4188]867    if { $_settings(-volumevisible) && $dataobj == $_first } {
[1694]868        SendCmd "volume state 1 $tag"
[1435]869    }
[3930]870    set _limits($tag) [list $info(min)  $info(max)]
871    set _limits(v)    [list $info(vmin) $info(vmax)]
[1013]872
[3339]873    unset _recvdDatasets($tag)
874    if { [array size _recvdDatasets] == 0 } {
[3930]875        updateTransferFunctions
[456]876    }
877}
878
879# ----------------------------------------------------------------------
[1376]880# USAGE: Rebuild
[436]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# ----------------------------------------------------------------------
[1376]886itcl::body Rappture::NanovisViewer::Rebuild {} {
[3498]887    set w [winfo width $itk_component(3dview)]
888    set h [winfo height $itk_component(3dview)]
889    if { $w < 2 || $h < 2 } {
[5092]890        update
[3498]891        $_dispatcher event -idle !rebuild
892        return
893    }
[1435]894
[1549]895    # Turn on buffering of commands to the server.  We don't want to
896    # be preempted by a server disconnect/reconnect (which automatically
[5098]897    # generates a new call to Rebuild).
[3421]898    StartBufferingCommands
[1549]899
[3498]900    if { $_width != $w || $_height != $h || $_reset } {
901        set _width $w
902        set _height $h
903        $_arcball resize $w $h
904        DoResize
905    }
[4709]906
[447]907    foreach dataobj [get] {
[3339]908        foreach cname [$dataobj components] {
909            set tag $dataobj-$cname
910            if { ![info exists _serverDatasets($tag)] } {
[1694]911                # Send the data as one huge base64-encoded mess -- yuck!
[3557]912                if { [$dataobj type] == "dx" } {
[3571]913                    if { ![$dataobj isvalid] } {
914                        puts stderr "??? $dataobj is invalid"
915                    }
[3940]916                    set data [$dataobj blob $cname]
[3557]917                } else {
918                    set data [$dataobj vtkdata $cname]
919                    if 0 {
920                        set f [open "/tmp/volume.vtk" "w"]
[4767]921                        fconfigure $f -translation binary -encoding binary
922                        puts -nonewline $f $data
[3557]923                        close $f
924                    }
925                }
[1694]926                set nbytes [string length $data]
[3421]927                if { $_reportClientInfo }  {
[3392]928                    set info {}
[4749]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]
[3392]934                    lappend info "dataset_label" [$dataobj hints label]
935                    lappend info "dataset_size"  $nbytes
[3394]936                    lappend info "dataset_tag"   $tag
[3392]937                    SendCmd "clientinfo [list $info]"
938                }
[3421]939                SendCmd "volume data follows $nbytes $tag"
[5134]940                SendData $data
[3339]941                set _recvdDatasets($tag) 1
942                set _serverDatasets($tag) 0
[1694]943            }
[5290]944            SetObjectStyle $dataobj $cname
[1694]945        }
[447]946    }
[3339]947    set _first [lindex [get] 0]
[3330]948    if { $_reset } {
[5098]949        #
950        # Reset the camera and other view parameters
951        #
[4188]952        set _settings(-qw)    $_view(-qw)
953        set _settings(-qx)    $_view(-qx)
954        set _settings(-qy)    $_view(-qy)
955        set _settings(-qz)    $_view(-qz)
[5098]956        set _settings(-xpan)  $_view(-xpan)
957        set _settings(-ypan)  $_view(-ypan)
958        set _settings(-zoom)  $_view(-zoom)
[3362]959
[4188]960        set q [ViewToQuaternion]
[3485]961        $_arcball quaternion $q
962        SendCmd "camera orient $q"
[3492]963        SendCmd "camera reset"
[5098]964        PanCamera
965        SendCmd "camera zoom $_view(-zoom)"
966
[5205]967        # Turn off cutplanes for all volumes
[3339]968        foreach axis {x y z} {
969            SendCmd "cutplane state 0 $axis"
970        }
[3899]971
[4188]972        InitSettings -light2side -ambient -diffuse -specularlevel \
973            -specularexponent -opacity -isosurfaceshading -gridvisible \
974            -axesvisible -xcutplanevisible -ycutplanevisible -zcutplanevisible \
975            -current
[3899]976
[5098]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        }
[1013]987    }
[3440]988    # Outline seems to need to be reset every update.
[5098]989    InitSettings -outlinevisible -cutplanesvisible
[3339]990    # nothing to send -- activate the proper ivol
991    SendCmd "volume state 0"
992    if {"" != $_first} {
[5098]993        set datasets [array names _serverDatasets $_first-*]
[3339]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)] } {
[3930]1001            updateTransferFunctions
[3339]1002        }
1003    }
[1435]1004    # Actually write the commands to the server socket.  If it fails, we don't
1005    # care.  We're finished here.
[1549]1006    blt::busy hold $itk_component(hull)
[3421]1007    StopBufferingCommands
[1549]1008    blt::busy release $itk_component(hull)
[3330]1009    set _reset 0
[436]1010}
1011
1012# ----------------------------------------------------------------------
[3339]1013# USAGE: CurrentDatasets ?-cutplanes?
[447]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# ----------------------------------------------------------------------
[3339]1019itcl::body Rappture::NanovisViewer::CurrentDatasets {{what -all}} {
[447]1020    set rlist ""
[1435]1021    if { $_first == "" } {
[1694]1022        return
[1435]1023    }
[3339]1024    foreach cname [$_first components] {
1025        set tag $_first-$cname
1026        if { [info exists _serverDatasets($tag)] && $_serverDatasets($tag) } {
[5188]1027            array set style {
[1694]1028                -cutplanes 1
1029            }
[5188]1030            array set style [lindex [$_first components -style $cname] 0]
1031            if { $what != "-cutplanes" || $style(-cutplanes) } {
[3339]1032                lappend rlist $tag
[1694]1033            }
1034        }
[447]1035    }
1036    return $rlist
1037}
1038
1039# ----------------------------------------------------------------------
[1376]1040# USAGE: Zoom in
1041# USAGE: Zoom out
1042# USAGE: Zoom reset
[436]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# ----------------------------------------------------------------------
[1376]1047itcl::body Rappture::NanovisViewer::Zoom {option} {
[436]1048    switch -- $option {
[1694]1049        "in" {
[4188]1050            set _view(-zoom) [expr {$_view(-zoom)*1.25}]
1051            set _settings(-zoom) $_view(-zoom)
1052            SendCmd "camera zoom $_view(-zoom)"
[1694]1053        }
1054        "out" {
[4188]1055            set _view(-zoom) [expr {$_view(-zoom)*0.8}]
1056            set _settings(-zoom) $_view(-zoom)
1057            SendCmd "camera zoom $_view(-zoom)"
[1694]1058        }
[1376]1059        "reset" {
[1694]1060            array set _view {
[4188]1061                -qw      0.853553
1062                -qx      -0.353553
1063                -qy      0.353553
1064                -qz      0.146447
[4509]1065                -xpan    0
1066                -ypan    0
[4188]1067                -zoom    1.0
[1694]1068            }
1069            if { $_first != "" } {
1070                set location [$_first hints camera]
1071                if { $location != "" } {
1072                    array set _view $location
1073                }
1074            }
[5098]1075            set q [ViewToQuaternion]
[3485]1076            $_arcball quaternion $q
1077            SendCmd "camera orient $q"
[3492]1078            SendCmd "camera reset"
[4188]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)
[1376]1086        }
[436]1087    }
1088}
1089
[1376]1090itcl::body Rappture::NanovisViewer::PanCamera {} {
[4188]1091    set x $_view(-xpan)
1092    set y $_view(-ypan)
[1376]1093    SendCmd "camera pan $x $y"
1094}
1095
[436]1096# ----------------------------------------------------------------------
[1376]1097# USAGE: Rotate click <x> <y>
1098# USAGE: Rotate drag <x> <y>
1099# USAGE: Rotate release <x> <y>
[436]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# ----------------------------------------------------------------------
[1376]1104itcl::body Rappture::NanovisViewer::Rotate {option x y} {
[436]1105    switch -- $option {
[1376]1106        click {
1107            $itk_component(3dview) configure -cursor fleur
[1435]1108            set _click(x) $x
1109            set _click(y) $y
[1376]1110        }
1111        drag {
[1435]1112            if {[array size _click] == 0} {
[1376]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                }
[436]1120
[1376]1121                if {[catch {
1122                    # this fails sometimes for no apparent reason
[1435]1123                    set dx [expr {double($x-$_click(x))/$w}]
1124                    set dy [expr {double($y-$_click(y))/$h}]
[1376]1125                }]} {
1126                    return
1127                }
[436]1128
[3485]1129                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
[4765]1130                QuaternionToView $q
[4188]1131                set _settings(-qw) $_view(-qw)
1132                set _settings(-qx) $_view(-qx)
1133                set _settings(-qy) $_view(-qy)
1134                set _settings(-qz) $_view(-qz)
[3485]1135                SendCmd "camera orient $q"
[436]1136
[1435]1137                set _click(x) $x
1138                set _click(y) $y
[1376]1139            }
1140        }
1141        release {
1142            Rotate drag $x $y
1143            $itk_component(3dview) configure -cursor ""
[1435]1144            catch {unset _click}
[1376]1145        }
1146        default {
1147            error "bad option \"$option\": should be click, drag, release"
1148        }
[436]1149    }
1150}
1151
1152# ----------------------------------------------------------------------
[1376]1153# USAGE: $this Pan click x y
1154#        $this Pan drag x y
1155#        $this Pan release x y
[1215]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# ----------------------------------------------------------------------
[1376]1160itcl::body Rappture::NanovisViewer::Pan {option x y} {
[1220]1161    # Experimental stuff
[1242]1162    set w [winfo width $itk_component(3dview)]
1163    set h [winfo height $itk_component(3dview)]
[1228]1164    if { $option == "set" } {
[1376]1165        set x [expr $x / double($w)]
1166        set y [expr $y / double($h)]
[4188]1167        set _view(-xpan) [expr $_view(-xpan) + $x]
1168        set _view(-ypan) [expr $_view(-ypan) + $y]
[1376]1169        PanCamera
[4188]1170        set _settings(-xpan) $_view(-xpan)
1171        set _settings(-ypan) $_view(-ypan)
[1376]1172        return
[1228]1173    }
[1254]1174    if { $option == "click" } {
[1435]1175        set _click(x) $x
1176        set _click(y) $y
[1376]1177        $itk_component(3dview) configure -cursor hand1
[1215]1178    }
1179    if { $option == "drag" || $option == "release" } {
[1435]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
[4188]1184        set _view(-xpan) [expr $_view(-xpan) - $dx]
1185        set _view(-ypan) [expr $_view(-ypan) - $dy]
[1376]1186        PanCamera
[4188]1187        set _settings(-xpan) $_view(-xpan)
1188        set _settings(-ypan) $_view(-ypan)
[1215]1189    }
1190    if { $option == "release" } {
[1376]1191        $itk_component(3dview) configure -cursor ""
[1215]1192    }
1193}
1194
1195# ----------------------------------------------------------------------
[3370]1196# USAGE: InitSettings <what> ?<value>?
[447]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# ----------------------------------------------------------------------
[3366]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# ----------------------------------------------------------------------
[3370]1215itcl::body Rappture::NanovisViewer::AdjustSetting {what {value ""}} {
[3371]1216    if {![isconnected]} {
1217        return
1218    }
[447]1219    switch -- $what {
[4188]1220        "-ambient" {
[5098]1221            # Other parts of the code use the ambient setting to
[4191]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)
[4188]1227            set val $_settings($what)
[3913]1228            set val [expr {0.01*$val}]
[3917]1229            foreach tag [GetDatasetsWithComponent $_current] {
1230                SendCmd "volume shading ambient $val $tag"
1231            }
[1376]1232        }
[4509]1233        "-axesvisible" {
1234            SendCmd "axis visible $_settings($what)"
1235        }
1236        "-background" {
1237            set bgcolor [$itk_component(background) value]
[5098]1238            array set fgcolors {
1239                "black" "white"
1240                "white" "black"
1241                "grey"  "black"
1242            }
[4509]1243            configure -plotbackground $bgcolor \
[5098]1244                -plotforeground $fgcolors($bgcolor)
1245            DrawLegend $_current
[4509]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)
[4989]1259            # We only set cutplanes on the first dataset.
[4509]1260            set datasets [CurrentDatasets -cutplanes]
1261            set tag [lindex $datasets 0]
1262            SendCmd "cutplane visible $bool $tag"
1263        }
[4188]1264        "-diffuse" {
[4191]1265            set _settings($_current${what}) $_settings($what)
[4188]1266            set val $_settings($what)
[3913]1267            set val [expr {0.01*$val}]
[3917]1268            foreach tag [GetDatasetsWithComponent $_current] {
1269                SendCmd "volume shading diffuse $val $tag"
1270            }
[3913]1271        }
[4509]1272        "-gridvisible" {
1273            SendCmd "grid visible $_settings($what)"
[3913]1274        }
[4509]1275        "-isosurfaceshading" {
[5289]1276            set val $_settings($what)
1277            foreach tag [GetDatasetsWithComponent $_current] {
1278                SendCmd "volume shading isosurface $val $tag"
1279            }
[4509]1280        }
1281        "-legendvisible" {
1282            if { $_settings($what) } {
1283                blt::table $itk_component(plotarea) \
1284                    0,0 $itk_component(3dview) -fill both \
[5098]1285                    1,0 $itk_component(legend) -fill x
[4509]1286                blt::table configure $itk_component(plotarea) r1 -resize none
1287            } else {
1288                blt::table forget $itk_component(legend)
[3917]1289            }
[3913]1290        }
[4188]1291        "-light2side" {
1292            set _settings($_current${what}) $_settings($what)
1293            set val $_settings($what)
[3917]1294            foreach tag [GetDatasetsWithComponent $_current] {
1295                SendCmd "volume shading light2side $val $tag"
1296            }
[3362]1297        }
[4188]1298        "-opacity" {
1299            set _settings($_current${what}) $_settings($what)
1300            set val $_settings($what)
[3371]1301            set sval [expr { 0.01 * double($val) }]
[3917]1302            foreach tag [GetDatasetsWithComponent $_current] {
1303                SendCmd "volume shading opacity $sval $tag"
1304            }
[1376]1305        }
[4188]1306        "-outlinevisible" {
1307            SendCmd "volume outline state $_settings($what)"
[1376]1308        }
[4188]1309        "-outlinecolor" {
1310            set rgb [Color2RGB $_settings($what)]
1311            SendCmd "volume outline color $rgb"
[1376]1312        }
[4509]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            }
[1376]1320        }
[4509]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            }
[3366]1327        }
[4509]1328        "-thickness" {
1329            set val $_settings($what)
1330            set _settings($_current${what}) $val
1331            updateTransferFunctions
[1376]1332        }
[4188]1333        "-volume" {
[3948]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.
[5098]1338            set datasets [CurrentDatasets]
[4188]1339            set bool $_settings($what)
[3948]1340            SendCmd "volume data state $bool $datasets"
1341            foreach cname $_componentsList {
[4188]1342                set _settings($cname-volumevisible) $bool
[3948]1343            }
[4188]1344            set _settings(-volumevisible) $bool
[1376]1345        }
[4188]1346        "-volumevisible" {
[5098]1347            # This is the component specific control.  It changes the
[3948]1348            # visibility of only the current component.
[4188]1349            set _settings($_current${what}) $_settings($what)
[3940]1350            foreach tag [GetDatasetsWithComponent $_current] {
[4188]1351                SendCmd "volume data state $_settings($what) $tag"
[3940]1352            }
1353        }
[4188]1354        "-xcutplanevisible" - "-ycutplanevisible" - "-zcutplanevisible" {
1355            set axis [string range $what 1 1]
1356            set bool $_settings($what)
[4989]1357            # We only set cutplanes on the first dataset.
[5098]1358            set datasets [CurrentDatasets -cutplanes]
[3371]1359            set tag [lindex $datasets 0]
1360            SendCmd "cutplane state $bool $axis $tag"
[1694]1361            if { $bool } {
1362                $itk_component(${axis}CutScale) configure -state normal \
1363                    -troughcolor white
[1435]1364            } else {
[1694]1365                $itk_component(${axis}CutScale) configure -state disabled \
1366                    -troughcolor grey82
[1435]1367            }
1368        }
[1376]1369        default {
1370            error "don't know how to fix $what"
1371        }
[447]1372    }
1373}
1374
1375# ----------------------------------------------------------------------
[1376]1376# USAGE: FixLegend
[456]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# ----------------------------------------------------------------------
[1376]1382itcl::body Rappture::NanovisViewer::FixLegend {} {
[1448]1383    set _resizeLegendPending 0
[459]1384    set lineht [font metrics $itk_option(-font) -linespace]
[1435]1385    set w [expr {$_width-20}]
[459]1386    set h [expr {[winfo height $itk_component(legend)]-20-$lineht}]
[3925]1387    if {$w > 0 && $h > 0 && $_first != "" } {
[3940]1388        if { [info exists _cname2transferFunction($_current)] } {
[3925]1389            SendCmd "legend $_current $w $h"
[1694]1390        }
[456]1391    }
1392}
1393
[459]1394#
[3940]1395# NameTransferFunction --
[1013]1396#
[1254]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
[1376]1401#       server parses the 3D data and sends back the limits via ReceiveData.]
[1013]1402#
[3940]1403itcl::body Rappture::NanovisViewer::NameTransferFunction { dataobj cname } {
[5188]1404    array set style {
[3395]1405        -color BCGYR
[1694]1406        -levels 6
[3362]1407        -markers ""
[459]1408    }
[3339]1409    set tag $dataobj-$cname
[5188]1410    array set style [lindex [$dataobj components -style $cname] 0]
[3940]1411    if { ![info exists _cname2transferFunction($cname)] } {
[3925]1412        # Get the colormap right now, since it doesn't change with marker
1413        # changes.
[5188]1414        set cmap [ColorsToColormap $style(-color)]
[5184]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]
[3925]1418    }
[3940]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    }
[3925]1426    return $cname
[1013]1427}
1428
1429#
[3940]1430# ComputeTransferFunction --
[1013]1431#
[3940]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.
[1013]1437#
[3940]1438itcl::body Rappture::NanovisViewer::ComputeTransferFunction { cname } {
[5184]1439    foreach {cmap amap} $_cname2transferFunction($cname) break
[3925]1440
[1013]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
[1254]1446    # reference.
[3940]1447    if { ![info exists _parsedFunction($cname)] } {
[5188]1448        array set style {
[3940]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
[5188]1456            array set style [lindex [$dataobj components -style $cname] 0]
[3940]1457        }
1458        eval $_transferFunctionEditors($cname) limits $_limits($cname)
[1694]1459        # Have to defer creation of isomarkers until we have data limits
[5188]1460        if { [info exists style(-markers)] &&
1461             [llength $style(-markers)] > 0 } {
1462            ParseMarkersOption $cname $style(-markers)
[1694]1463        } else {
[5188]1464            ParseLevelsOption $cname $style(-levels)
[1694]1465        }
[5098]1466
[1254]1467    }
[5184]1468    set amap [ComputeAlphamap $cname]
1469    set _cname2transferFunction($cname) [list $cmap $amap]
1470    SendCmd [list transfunc define $cname $cmap $amap]
[459]1471}
1472
[4088]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
[4166]1479    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
[4088]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
[5098]1487    $_transferFunctionEditors($_current) deleteMarker $x $y
[4088]1488}
1489
[459]1490# ----------------------------------------------------------------------
[436]1491# CONFIGURATION OPTION: -plotbackground
1492# ----------------------------------------------------------------------
1493itcl::configbody Rappture::NanovisViewer::plotbackground {
[907]1494    if { [isconnected] } {
[4163]1495        set color $itk_option(-plotbackground)
1496        set rgb [Color2RGB $color]
1497        SendCmd "screen bgcolor $rgb"
1498        $itk_component(legend) configure -background $color
[907]1499    }
[436]1500}
1501
1502# ----------------------------------------------------------------------
1503# CONFIGURATION OPTION: -plotforeground
1504# ----------------------------------------------------------------------
1505itcl::configbody Rappture::NanovisViewer::plotforeground {
[907]1506    if { [isconnected] } {
[4163]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"
[5098]1512        $itk_component(legend) itemconfigure labels -fill $color
1513        $itk_component(legend) itemconfigure limits -fill $color
[907]1514    }
[436]1515}
1516
1517# ----------------------------------------------------------------------
1518# CONFIGURATION OPTION: -plotoutline
1519# ----------------------------------------------------------------------
1520itcl::configbody Rappture::NanovisViewer::plotoutline {
[907]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] } {
[1694]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        }
[436]1532    }
1533}
[878]1534
[965]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#
[3925]1540itcl::body Rappture::NanovisViewer::ParseLevelsOption { cname levels } {
[965]1541    set c $itk_component(legend)
[3940]1542    set list {}
[965]1543    regsub -all "," $levels " " levels
[972]1544    if {[string is int $levels]} {
[1694]1545        for {set i 1} { $i <= $levels } {incr i} {
[3940]1546            lappend list [expr {double($i)/($levels+1)}]
[1694]1547        }
[972]1548    } else {
[1694]1549        foreach x $levels {
[3940]1550            lappend list $x
[1694]1551        }
[878]1552    }
[3940]1553    set _parsedFunction($cname) 1
1554    $_transferFunctionEditors($cname) addMarkers $list
[4166]1555    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
[965]1556}
1557
1558#
1559# The -markers option takes a list of zero or more values (the values
[5098]1560# may be separated either by spaces or commas) that have the following
[965]1561# format:
1562#
[1254]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.
[965]1569#
[3925]1570itcl::body Rappture::NanovisViewer::ParseMarkersOption { cname markers } {
[878]1571    set c $itk_component(legend)
[3940]1572    set list {}
1573    foreach { min max } $_limits($cname) break
[965]1574    regsub -all "," $markers " " markers
1575    foreach marker $markers {
[1694]1576        set n [scan $marker "%g%s" value suffix]
1577        if { $n == 2 && $suffix == "%" } {
[5098]1578            # $n% : Set relative value (0..1).
[3940]1579            lappend list [expr {$value * 0.01}]
[1694]1580        } else {
[3940]1581            # $n : absolute value, compute relative
1582            lappend list  [expr {(double($value)-$min)/($max-$min)]}
[1694]1583        }
[878]1584    }
[3940]1585    set _parsedFunction($cname) 1
1586    $_transferFunctionEditors($cname) addMarkers $list
[4166]1587    $itk_component(legend) itemconfigure labels -fill $itk_option(-plotforeground)
[878]1588}
1589
1590# ----------------------------------------------------------------------
[5246]1591# USAGE: UpdateTransferFuncs
[878]1592# ----------------------------------------------------------------------
[3930]1593itcl::body Rappture::NanovisViewer::updateTransferFunctions {} {
[972]1594    $_dispatcher event -idle !send_transfunc
[878]1595}
1596
[1376]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
[4188]1606    set ::Rappture::NanovisViewer::_settings(-isosurfaceshading) 0
[1376]1607    checkbutton $inner.isosurface \
1608        -text "Isosurface shading" \
[4188]1609        -variable [itcl::scope _settings(-isosurfaceshading)] \
1610        -command [itcl::code $this AdjustSetting -isosurfaceshading] \
[1694]1611        -font "Arial 9"
[1376]1612
1613    checkbutton $inner.axes \
1614        -text "Axes" \
[4188]1615        -variable [itcl::scope _settings(-axesvisible)] \
1616        -command [itcl::code $this AdjustSetting -axesvisible] \
[1694]1617        -font "Arial 9"
[1376]1618
1619    checkbutton $inner.grid \
1620        -text "Grid" \
[4188]1621        -variable [itcl::scope _settings(-gridvisible)] \
1622        -command [itcl::code $this AdjustSetting -gridvisible] \
[1694]1623        -font "Arial 9"
[1376]1624
1625    checkbutton $inner.outline \
1626        -text "Outline" \
[4188]1627        -variable [itcl::scope _settings(-outlinevisible)] \
1628        -command [itcl::code $this AdjustSetting -outlinevisible] \
[1694]1629        -font "Arial 9"
[1376]1630
1631    checkbutton $inner.legend \
1632        -text "Legend" \
[4188]1633        -variable [itcl::scope _settings(-legendvisible)] \
1634        -command [itcl::code $this AdjustSetting -legendvisible] \
[1694]1635        -font "Arial 9"
[1376]1636
1637    checkbutton $inner.volume \
1638        -text "Volume" \
[4188]1639        -variable [itcl::scope _settings(-volume)] \
1640        -command [itcl::code $this AdjustSetting -volume] \
[1694]1641        -font "Arial 9"
[1376]1642
[5098]1643    label $inner.background_l -text "Background" -font "Arial 9"
[4121]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"            \
[5098]1650        "grey"               "grey"
[4121]1651
[4188]1652    $itk_component(background) value $_settings(-background)
1653    bind $inner.background <<Value>> \
1654        [itcl::code $this AdjustSetting -background]
[4121]1655
[1376]1656    blt::table $inner \
[5292]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
[1376]1664
1665    blt::table configure $inner r* -resize none
[4121]1666    blt::table configure $inner r6 -resize expand
[1376]1667}
1668
1669itcl::body Rappture::NanovisViewer::BuildVolumeTab {} {
1670    set inner [$itk_component(main) insert end \
1671        -title "Volume Settings" \
[1391]1672        -icon [Rappture::icon volume-on]]
[1376]1673    $inner configure -borderwidth 4
1674
[4709]1675    set fg [option get $itk_component(hull) font Font]
1676    #set bfg [option get $itk_component(hull) boldFont Font]
[1376]1677
[3948]1678    label $inner.lighting_l \
1679        -text "Lighting / Material Properties" \
[5098]1680        -font "Arial 9 bold"
[3915]1681
[4709]1682    checkbutton $inner.light2side -text "Two-sided lighting" -font $fg \
[4188]1683        -variable [itcl::scope _settings(-light2side)] \
1684        -command [itcl::code $this AdjustSetting -light2side]
[3362]1685
[4709]1686    checkbutton $inner.visibility -text "Visible" -font $fg \
[4188]1687        -variable [itcl::scope _settings(-volumevisible)] \
[4713]1688        -command [itcl::code $this AdjustSetting -volumevisible]
[3940]1689
[4709]1690    label $inner.ambient_l -text "Ambient" -font $fg
[3913]1691    ::scale $inner.ambient -from 0 -to 100 -orient horizontal \
[4188]1692        -variable [itcl::scope _settings(-ambient)] \
1693        -showvalue off -command [itcl::code $this AdjustSetting -ambient] \
[3948]1694        -troughcolor grey92
[1376]1695
[4709]1696    label $inner.diffuse_l -text "Diffuse" -font $fg
[3913]1697    ::scale $inner.diffuse -from 0 -to 100 -orient horizontal \
[4188]1698        -variable [itcl::scope _settings(-diffuse)] \
1699        -showvalue off -command [itcl::code $this AdjustSetting -diffuse] \
[3948]1700        -troughcolor grey92
[3913]1701
[4709]1702    label $inner.specularLevel_l -text "Specular" -font $fg
[3913]1703    ::scale $inner.specularLevel -from 0 -to 100 -orient horizontal \
[4188]1704        -variable [itcl::scope _settings(-specularlevel)] \
1705        -showvalue off \
1706        -command [itcl::code $this AdjustSetting -specularlevel] \
[3948]1707        -troughcolor grey92
[3913]1708
[4709]1709    label $inner.specularExponent_l -text "Shininess" -font $fg
[3913]1710    ::scale $inner.specularExponent -from 10 -to 128 -orient horizontal \
[4188]1711        -variable [itcl::scope _settings(-specularexponent)] \
[3917]1712        -showvalue off \
[4188]1713        -command [itcl::code $this AdjustSetting -specularexponent] \
[3948]1714        -troughcolor grey92
[3913]1715
[4713]1716    # Opacity
[4709]1717    label $inner.opacity_l -text "Opacity" -font $fg
[4188]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] \
[3948]1721        -troughcolor grey92
[1376]1722
[3948]1723    label $inner.transferfunction_l \
[5098]1724        -text "Transfer Function" -font "Arial 9 bold"
[3917]1725
[4713]1726    # Tooth thickness
[4709]1727    label $inner.thin -text "Thin" -font $fg
[1376]1728    ::scale $inner.thickness -from 0 -to 1000 -orient horizontal \
[4188]1729        -variable [itcl::scope _settings(-thickness)] \
1730        -showvalue off -command [itcl::code $this AdjustSetting -thickness] \
[3948]1731        -troughcolor grey92
[4709]1732    label $inner.thick -text "Thick" -font $fg
[1376]1733
[5098]1734    # Colormap
[4709]1735    label $inner.colormap_l -text "Colormap" -font $fg
[3366]1736    itk_component add colormap {
1737        Rappture::Combobox $inner.colormap -width 10 -editable no
1738    }
1739
[4336]1740    $inner.colormap choices insert end [GetColormapList -includeDefault -includeNone]
[3366]1741    bind $inner.colormap <<Value>> \
[4188]1742        [itcl::code $this AdjustSetting -colormap]
[3925]1743    $itk_component(colormap) value "default"
[4188]1744    set _settings(-colormap) "default"
[3366]1745
[4713]1746    # Component
[4709]1747    label $inner.volcomponents_l -text "Component" -font $fg
[3917]1748    itk_component add volcomponents {
1749        Rappture::Combobox $inner.volcomponents -editable no
1750    }
1751    bind $inner.volcomponents <<Value>> \
[4188]1752        [itcl::code $this AdjustSetting -current]
[3917]1753
[1376]1754    blt::table $inner \
[3948]1755        0,0 $inner.volcomponents_l -anchor e -cspan 2 \
[5294]1756        0,2 $inner.volcomponents -cspan 3 -fill x \
[3948]1757        1,1 $inner.lighting_l -anchor w -cspan 4 \
[5294]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 \
[3917]1766        6,1 $inner.light2side -cspan 3 -anchor w \
[3940]1767        7,1 $inner.visibility -cspan 3 -anchor w \
[5294]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 \
[5098]1775        11,4 $inner.thick -anchor w
[1376]1776
[3917]1777    blt::table configure $inner c* r* -resize none
1778    blt::table configure $inner r* -pady { 2 0 }
[3940]1779    blt::table configure $inner c2 c3 r12 -resize expand
[3917]1780    blt::table configure $inner c0 -width .1i
[1376]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
[3901]1789    checkbutton $inner.visible \
1790        -text "Show Cutplanes" \
[4188]1791        -variable [itcl::scope _settings(-cutplanesvisible)] \
1792        -command [itcl::code $this AdjustSetting -cutplanesvisible] \
[3901]1793        -font "Arial 9"
1794
[1376]1795    # X-value slicer...
1796    itk_component add xCutButton {
[1435]1797        Rappture::PushButton $inner.xbutton \
[1694]1798            -onimage [Rappture::icon x-cutplane] \
1799            -offimage [Rappture::icon x-cutplane] \
[4188]1800            -command [itcl::code $this AdjustSetting -xcutplanevisible] \
1801            -variable [itcl::scope _settings(-xcutplanevisible)]
[1376]1802    }
1803    Rappture::Tooltip::for $itk_component(xCutButton) \
1804        "Toggle the X cut plane on/off"
[3899]1805    $itk_component(xCutButton) select
[1376]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 \
[1435]1811            -command [itcl::code $this Slice move x] \
[4188]1812            -variable [itcl::scope _settings(-xcutplaneposition)]
[1376]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 {
[1435]1825        Rappture::PushButton $inner.ybutton \
[1694]1826            -onimage [Rappture::icon y-cutplane] \
1827            -offimage [Rappture::icon y-cutplane] \
[4188]1828            -command [itcl::code $this AdjustSetting -ycutplanevisible] \
1829            -variable [itcl::scope _settings(-ycutplanevisible)]
[1376]1830    }
1831    Rappture::Tooltip::for $itk_component(yCutButton) \
1832        "Toggle the Y cut plane on/off"
[3899]1833    $itk_component(yCutButton) select
[1376]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 \
[1435]1839            -command [itcl::code $this Slice move y] \
[4188]1840            -variable [itcl::scope _settings(-ycutplaneposition)]
[1376]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 {
[1435]1853        Rappture::PushButton $inner.zbutton \
[1694]1854            -onimage [Rappture::icon z-cutplane] \
1855            -offimage [Rappture::icon z-cutplane] \
[4188]1856            -command [itcl::code $this AdjustSetting -zcutplanevisible] \
1857            -variable [itcl::scope _settings(-zcutplanevisible)]
[1376]1858    }
1859    Rappture::Tooltip::for $itk_component(zCutButton) \
1860        "Toggle the Z cut plane on/off"
[3899]1861    $itk_component(zCutButton) select
[1376]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 \
[1435]1867            -command [itcl::code $this Slice move z] \
[4188]1868            -variable [itcl::scope _settings(-zcutplaneposition)]
[1376]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 \
[5294]1879        0,1 $inner.visible -anchor w -pady 2 -cspan 4 \
[3901]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)
[1376]1886
[3901]1887    blt::table configure $inner r0 r1 r2 c* -resize none
1888    blt::table configure $inner r3 c4 -resize expand
[1376]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
[3517]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
[3492]1906    }
[3517]1907
[3492]1908    blt::table $inner \
[3533]1909        0,0 $inner.view_l -anchor e -pady 2 \
1910        0,1 $inner.view -anchor w -pady 2
[4771]1911    blt::table configure $inner r0 -resize none
[3492]1912
[3517]1913    set row 1
1914    set labels { qw qx qy qz xpan ypan zoom }
[1376]1915    foreach tag $labels {
[1694]1916        label $inner.${tag}label -text $tag -font "Arial 9"
1917        entry $inner.${tag} -font "Arial 9"  -bg white \
[4188]1918            -textvariable [itcl::scope _settings(-$tag)]
[3454]1919        bind $inner.${tag} <Return> \
[4343]1920            [itcl::code $this camera set -${tag}]
[3454]1921        bind $inner.${tag} <KP_Enter> \
[4343]1922            [itcl::code $this camera set -${tag}]
[1694]1923        blt::table $inner \
1924            $row,0 $inner.${tag}label -anchor e -pady 2 \
1925            $row,1 $inner.${tag} -anchor w -pady 2
[1376]1926        blt::table configure $inner r$row -resize none
[1694]1927        incr row
[1376]1928    }
[3032]1929
[4771]1930    blt::table configure $inner c* -resize none
[1376]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}]
[3339]1953            set datasets [CurrentDatasets -cutplanes]
1954            set tag [lindex $datasets 0]
1955            SendCmd "cutplane position $newpos $axis $tag"
[1376]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}
[1377]1973
1974itcl::body Rappture::NanovisViewer::DoResize {} {
[3407]1975    $_arcball resize $_width $_height
[3478]1976    SendCmd "screen size $_width $_height"
[1448]1977    set _resizePending 0
[1377]1978}
1979
1980itcl::body Rappture::NanovisViewer::EventuallyResize { w h } {
[1448]1981    set _width $w
1982    set _height $h
[3362]1983    $_arcball resize $w $h
[1448]1984    if { !$_resizePending } {
[1694]1985        $_dispatcher event -idle !resize
1986        set _resizePending 1
[1377]1987    }
1988}
1989
[3925]1990itcl::body Rappture::NanovisViewer::EventuallyRedrawLegend {} {
[1448]1991    if { !$_resizeLegendPending } {
[1694]1992        $_dispatcher event -idle !legend
1993        set _resizeLegendPending 1
[1448]1994    }
[1377]1995}
[1423]1996
[5098]1997#  camera --
[1423]1998#
1999itcl::body Rappture::NanovisViewer::camera {option args} {
[5098]2000    switch -- $option {
[1694]2001        "show" {
2002            puts [array get _view]
2003        }
2004        "set" {
[4188]2005            set what [lindex $args 0]
2006            set x $_settings($what)
[1694]2007            set code [catch { string is double $x } result]
2008            if { $code != 0 || !$result } {
[4188]2009                set _settings($what) $_view($what)
[1694]2010                return
2011            }
[4188]2012            switch -- $what {
2013                "-xpan" - "-ypan" {
2014                    set _view($what) $_settings($what)
[1694]2015                    PanCamera
2016                }
[4188]2017                "-qx" - "-qy" - "-qz" - "-qw" {
2018                    set _view($what) $_settings($what)
2019                    set q [ViewToQuaternion]
[3362]2020                    $_arcball quaternion $q
2021                    SendCmd "camera orient $q"
2022                }
[4188]2023                "-zoom" {
2024                    set _view($what) $_settings($what)
2025                    SendCmd "camera zoom $_view($what)"
[1694]2026                }
2027            }
2028        }
[1423]2029    }
2030}
[1535]2031
[5098]2032itcl::body Rappture::NanovisViewer::SetOrientation { side } {
[3517]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    }
[4188]2041    foreach name { -qw -qx -qy -qz } value $positions($side) {
[3517]2042        set _view($name) $value
[5098]2043    }
[4188]2044    set q [ViewToQuaternion]
[3492]2045    $_arcball quaternion $q
[5098]2046    SendCmd "camera orient $q"
[3533]2047    SendCmd "camera reset"
[4188]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)
[3492]2054}
[3517]2055
[3948]2056#
2057# InitComponentSettings --
2058#
[4709]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.
[3948]2062#
[5098]2063itcl::body Rappture::NanovisViewer::InitComponentSettings { cname } {
[5201]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    }
[3917]2077}
2078
[3948]2079#
2080# SwitchComponent --
2081#
[4709]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.
[3948]2085#
[5098]2086itcl::body Rappture::NanovisViewer::SwitchComponent { cname } {
[3925]2087    if { ![info exists _settings($cname-ambient)] } {
[3917]2088        InitComponentSettings $cname
2089    }
[3925]2090    # _settings variables change widgets, except for colormap
[4188]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)
[3925]2101    set _current $cname;                # Reset the current component
[3917]2102}
2103
[3948]2104#
2105# BuildVolumeComponents --
2106#
[4709]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.
[3948]2113#
[5098]2114itcl::body Rappture::NanovisViewer::BuildVolumeComponents {} {
[3917]2115    $itk_component(volcomponents) choices delete 0 end
[3925]2116    foreach name $_componentsList {
[3917]2117        $itk_component(volcomponents) choices insert end $name $name
2118    }
[3925]2119    set _current [lindex $_componentsList 0]
2120    $itk_component(volcomponents) value $_current
[3948]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
[5098]2124        # component.
[3948]2125        blt::table forget $parent.volcomponents_l $parent.volcomponents
2126    } else {
[5098]2127        # Pack the components label and dropdown into the table there's
2128        # more than one component to select.
[3948]2129        blt::table $parent \
2130            0,0 $parent.volcomponents_l -anchor e -cspan 2 \
[5098]2131            0,2 $parent.volcomponents -cspan 3 -fill x
[3948]2132    }
[3917]2133}
2134
[3948]2135#
2136# GetDatasetsWithComponents --
2137#
[4709]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.
[3948]2142#
[5098]2143itcl::body Rappture::NanovisViewer::GetDatasetsWithComponent { cname } {
[3917]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}
[3925]2156
[3940]2157#
2158# HideAllMarkers --
2159#
[4709]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.
[3940]2163#
[5098]2164itcl::body Rappture::NanovisViewer::HideAllMarkers {} {
[3940]2165    foreach cname [array names _transferFunctionEditors] {
[5098]2166        $_transferFunctionEditors($cname) hideMarkers
[3925]2167    }
2168}
2169
[5098]2170itcl::body Rappture::NanovisViewer::GetColormap { cname color } {
[3925]2171    if { $color == "default" } {
2172        return $_cname2defaultcolormap($cname)
2173    }
2174    return [ColorsToColormap $color]
2175}
2176
[5098]2177itcl::body Rappture::NanovisViewer::ResetColormap { cname color } {
[3925]2178    # Get the current transfer function
[3940]2179    if { ![info exists _cname2transferFunction($cname)] } {
[3925]2180        return
2181    }
[5184]2182    foreach { cmap amap } $_cname2transferFunction($cname) break
[3925]2183    set cmap [GetColormap $cname $color]
[5184]2184    set _cname2transferFunction($cname) [list $cmap $amap]
2185    SendCmd [list transfunc define $cname $cmap $amap]
[3925]2186    EventuallyRedrawLegend
2187}
2188
[5098]2189itcl::body Rappture::NanovisViewer::ComputeAlphamap { cname } {
[3940]2190    if { ![info exists _transferFunctionEditors($cname)] } {
[3925]2191        return [list 0.0 0.0 1.0 1.0]
2192    }
2193    if { ![info exists _settings($cname-ambient)] } {
2194        InitComponentSettings $cname
2195    }
2196
[3940]2197    set isovalues [$_transferFunctionEditors($cname) values]
[3925]2198
[4725]2199    # Transfer function should be normalized with [0,1] range
2200    # The volume shading opacity setting is used to scale opacity
[4728]2201    # in the volume shader.
[4191]2202    set max 1.0
[3925]2203
[4191]2204    # Use the component-wise thickness setting from the slider
2205    # settings widget
[3925]2206    # Scale values between 0.00001 and 0.01000
2207    set delta [expr {double($_settings($cname-thickness)) * 0.0001}]
[5098]2208
[3925]2209    set first [lindex $isovalues 0]
2210    set last [lindex $isovalues end]
[5184]2211    set amap ""
[3925]2212    if { $first == "" || $first != 0.0 } {
[5184]2213        lappend amap 0.0 0.0
[3925]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
[5184]2241        lappend amap $x1 0.0
2242        lappend amap $x2 $max
2243        lappend amap $x3 $max
2244        lappend amap $x4 0.0
[3925]2245    }
2246    if { $last == "" || $last != 1.0 } {
[5184]2247        lappend amap 1.0 0.0
[3925]2248    }
[5184]2249    return $amap
[3925]2250}
[5290]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) $style(-opacity)
2261    set tag $dataobj-$cname
2262    SendCmd "volume shading opacity $_settings($cname-opacity) $tag"
2263    NameTransferFunction $dataobj $cname
2264}
Note: See TracBrowser for help on using the repository browser.